/* gcompris - menu2.c
*
* Copyright (C) 2000, 2008 Bruno Coudoin, Yves Combe
*
* 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 .
*/
/**
* The menu display icons of each boards and let the user select one
*
* Seen from gcompris, the menu is a board like another.
*/
#include
#include "gcompris/gcompris.h"
#include "gcompris/gcompris_config.h"
#define SOUNDLISTFILE PACKAGE
#define MENU_PER_LINE 5
typedef struct {
GooCanvasItem *bg; /* Background */
GooCanvasItem *boardname_item;
GooCanvasItem *description_item;
GooCanvasItem *author_item;
} MenuItems;
static MenuItems *menuitems;
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static gchar *menu_position = NULL;
/* We don't wan't the callback on boards to be accepted until the menu is fully displayed */
static gboolean menu_displayed = FALSE;
static void menu_start (GcomprisBoard *agcomprisBoard);
static void menu_pause (gboolean pause);
static void menu_end (void);
static gboolean menu_is_our_board (GcomprisBoard *gcomprisBoard);
static void menu_config(void);
static void menu_config_start(GcomprisBoard *agcomprisBoard,
GcomprisProfile *aProfile);
static void menu_config_stop(void);
static void menu_create_item(GooCanvasItem *parent, MenuItems *menuitems, GcomprisBoard *board);
static gboolean next_spot();
static void create_info_area(GooCanvasItem *parent, MenuItems *menuitems);
static gboolean on_enter_notify (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventCrossing *event,
MenuItems *menuitems);
static gboolean on_leave_notify (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventCrossing *event,
MenuItems *menuitems);
static gint item_event(GooCanvasItem *item, GdkEvent *event, MenuItems *menuitems);
static void display_board_icon(GcomprisBoard *board, MenuItems *menuitems);
static void display_section (gchar *path);
static void display_welcome (MenuItems *menuitems);
static void create_panel(GooCanvasItem *parent);
static void create_top(GooCanvasItem *parent, gchar *path);
static GooCanvasItem *menu_difficulty_display(GooCanvasItem *parent,
double x, double y,
double ratio,
gint difficulty);
static GooCanvasItem *menu_demo_display(GooCanvasItem *parent,
gdouble x, gdouble y);
static double current_x = 0.0;
static double current_y = 0.0;
static double current_top_x = 0.0;
static double current_top_y = 0.0;
static double panel_x, panel_y, panel_w, panel_h;
static double top_x, top_y, top_w, top_h, top_int_x;
static double display_x, display_y, display_w, display_h, display_int_x, display_int_y;
static double info_x, info_y, info_w, info_h;
static double icon_size, icon_size_panel, icon_size_top, top_arrow_size;
static gdouble get_ratio(GdkPixbuf *pixmap, gdouble size);
#define P_X 8
#define P_Y 35
#define P_W 80
#define P_H 480
#define T_X 175
#define T_Y 10
#define T_W 550
#define T_H 55
#define T_INT_X 5
#define T_ARROW_SIZE 20
#define D_X 175
#define D_Y 70
#define D_W 550
#define D_H 320
#define D_INT_X 50
#define D_INT_Y 5
#define I_X 140
#define I_Y 390
#define I_W 600
#define I_H 125
#define ICON_SIZE 100
#define ICON_SIZE_PANEL 50
#define ICON_SIZE_TOP 50
static GooCanvasItem *boardRootItem = NULL;
static GooCanvasItem *actualSectionItem = NULL;
static GList *panelBoards = NULL;
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
N_("Main Menu Second Version"),
N_("Select a Board"),
"Bruno Coudoin ",
NULL,
NULL,
NULL,
NULL,
menu_start,
menu_pause,
menu_end,
menu_is_our_board,
NULL,
NULL,
NULL,
menu_config,
NULL,
menu_config_start,
menu_config_stop
};
/*
* Main entry point mandatory for each Gcompris's board
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO(menu)
/*
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
*
*/
static void menu_pause (gboolean pause)
{
if(gcomprisBoard==NULL)
return;
board_paused = pause;
}
/*
*/
static void menu_start (GcomprisBoard *agcomprisBoard)
{
current_x = 0.0;
current_y = 0.0;
/* initialisations */
/* in case we will make this parametrable */
panel_x = P_X;
panel_y = P_Y;
panel_w = P_W;
panel_h = P_H;
top_x = T_X;
top_y = T_Y;
top_w = T_W;
top_h = T_H;
top_int_x = T_INT_X;
top_arrow_size = T_ARROW_SIZE;
display_x = D_X;
display_y = D_Y;
display_w = D_W;
display_h = D_H;
display_int_x = D_INT_X;
display_int_y = D_INT_Y;
info_x = I_X;
info_y = I_Y;
info_w = I_W;
info_h = I_H;
icon_size = ICON_SIZE;
icon_size_panel = ICON_SIZE_PANEL;
icon_size_top = ICON_SIZE_TOP;
if(agcomprisBoard != NULL)
{
RsvgHandle *svg_handle;
gcomprisBoard=agcomprisBoard;
/* set initial values for this level */
gcomprisBoard->level = 1;
gcomprisBoard->maxlevel=1;
/* Set back the bar to it's original location */
gc_bar_set(GC_BAR_CONFIG|GC_BAR_ABOUT);
menuitems = g_new(MenuItems, 1);
svg_handle = gc_skin_rsvg_get();
gc_set_background_by_id (goo_canvas_get_root_item(gcomprisBoard->canvas),
svg_handle,
"#BACKGROUND");
boardRootItem = \
goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
NULL);
g_object_set_data_full(G_OBJECT (boardRootItem),
"menuitems", menuitems, g_free);
goo_canvas_svg_new (boardRootItem,
svg_handle,
"svg-id", "#SELECTOR",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
goo_canvas_svg_new (boardRootItem,
svg_handle,
"svg-id", "#BUTTON_HORIZONTAL",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
create_info_area(boardRootItem, menuitems);
create_panel(boardRootItem);
if (menu_position)
display_section(menu_position);
else
display_welcome(menuitems);
{
gchar *text = g_strdup_printf(_("Number of activities: %d"),
gc_board_get_number_of_activity());
goo_canvas_text_new (boardRootItem,
text,
BOARDWIDTH - 10,
BOARDHEIGHT - 10,
-1,
GTK_ANCHOR_EAST,
"font", gc_skin_font_board_tiny,
"fill-color-rgba", gc_skin_get_color("menu/text"),
"alignment", PANGO_ALIGN_RIGHT,
NULL);
g_free(text);
}
menu_pause(FALSE);
}
}
static void
create_panel(GooCanvasItem *parent)
{
int n_sections;
GList *list = NULL;
GcomprisBoard *board;
GdkPixbuf *pixmap = NULL;
GooCanvasItem *item;
gdouble x, y;
gint int_y;
GcomprisProperties *properties = gc_prop_get();
g_assert(properties);
/* In normal mode, we show all the sections in panel */
/* in direct submenu access, we show the icon of the submenu */
if (strcmp(properties->root_menu,"/")==0)
panelBoards = gc_menu_getlist(properties->root_menu);
else
panelBoards = g_list_append(list, gcomprisBoard);
n_sections = g_list_length(panelBoards);
if (n_sections == 0)
return;
if ( (panel_h/n_sections) <= icon_size_panel)
{
icon_size_panel = panel_h / n_sections;
int_y = 0;
}
else
{
int_y = (panel_h - n_sections*icon_size_panel)/n_sections;
}
x = panel_x + panel_w * 0.25;
y = panel_y + int_y / 2.0 - 20;
for (list = panelBoards; list != NULL; list = list->next)
{
board = (GcomprisBoard *) list->data;
pixmap = gc_pixmap_load(board->icon_name);
item = goo_canvas_image_new (parent,
pixmap,
x,
y,
NULL);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
y += int_y + icon_size_panel;
g_object_set_data (G_OBJECT (item), "board", board);
g_signal_connect(item, "button_press_event",
(GCallback) item_event,
menuitems);
g_signal_connect (item, "enter_notify_event",
(GCallback) on_enter_notify, menuitems);
g_signal_connect (item, "leave_notify_event",
(GCallback) on_leave_notify, menuitems);
if ( ! gc_board_is_demo_only(board) )
gc_item_focus_init(item, NULL);
}
}
static void
display_section (gchar *path)
{
GList *boardlist; /* List of Board */
menu_displayed = FALSE;
boardlist = gc_menu_getlist(path);
if (actualSectionItem)
goo_canvas_item_remove(actualSectionItem);
current_x = 0.0;
current_y = 0.0;
current_top_x = 0.0;
current_top_y = 0.0;
actualSectionItem = goo_canvas_group_new (boardRootItem,
NULL);
create_top (actualSectionItem, path);
g_list_foreach (boardlist, (GFunc) display_board_icon, menuitems);
if (strcmp(path,"home")!=0)
g_list_free(boardlist);
menu_displayed = TRUE;
}
static void
menu_end ()
{
if(boardRootItem!=NULL)
goo_canvas_item_remove(boardRootItem);
boardRootItem = NULL;
actualSectionItem = NULL;
g_list_free(panelBoards);
}
static gboolean
menu_is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_ascii_strcasecmp(gcomprisBoard->type, "menu")==0)
{
/* Set the plugin entry */
gcomprisBoard->plugin=&menu_bp;
return TRUE;
}
}
return FALSE;
}
static void
menu_config ()
{
if(gcomprisBoard!=NULL)
{
menu_pause(TRUE);
gc_config_start();
}
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
static void display_board_icon(GcomprisBoard *board, MenuItems *menuitems)
{
/* FIXME: Check board is available for the current profile */
if (board!=NULL
&& gc_board_check_file(board))
{
menu_create_item(actualSectionItem, menuitems, board);
}
}
/*
* Calculate the next stop where to place an item
* return false if there is no more space left
*/
static gboolean next_spot()
{
if(current_x==0.0)
{
/* Initialisation case */
current_x = display_x + icon_size/2.0;
current_y = display_y + icon_size/2.0;
return(TRUE);
}
current_x += icon_size + display_int_x;
if(current_x > display_x + display_w - icon_size/2.0)
{
current_x = display_x + icon_size/2.0;
current_y += icon_size + display_int_y;
}
if ( current_y > display_y + display_h - icon_size/2.0 )
return FALSE;
else
return TRUE;
}
static void menu_create_item(GooCanvasItem *parent, MenuItems *menuitems, GcomprisBoard *board)
{
GdkPixbuf *menu_pixmap = NULL;
GooCanvasItem *menu_button;
int difficulty;
gdouble ratio, pixmap_w, pixmap_h;
menu_pixmap = gc_pixmap_load(board->icon_name);
ratio = get_ratio (menu_pixmap, icon_size);
pixmap_w = gdk_pixbuf_get_width(menu_pixmap) * ratio;
pixmap_h = gdk_pixbuf_get_height(menu_pixmap) * ratio;
next_spot();
menu_button = goo_canvas_image_new (parent,
menu_pixmap,
current_x - pixmap_w/2,
current_y - pixmap_h/2,
NULL);
goo_canvas_item_scale(menu_button, ratio, ratio);
// display board availability due to sound voice not present
if(board->mandatory_sound_file)
{
gchar *soundfile = NULL;
/* We search a fixed path sound file */
soundfile = gc_file_find_absolute(board->mandatory_sound_file);
if (!soundfile || !gc_prop_get()->fx) {
GooCanvasItem *item = \
goo_canvas_svg_new (parent,
gc_skin_rsvg_get(),
"svg-id", "#SOUND_UNCHECKED",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
GooCanvasBounds bounds;
goo_canvas_item_get_bounds(item, &bounds);
SET_ITEM_LOCATION(item,
current_x - pixmap_w/2 - 25 -
(bounds.x2 - bounds.x1)/2,
current_y - pixmap_h/2 + 28-
(bounds.y2 - bounds.y1)/2);
}
g_free(soundfile);
}
// display menu icon ========================== BEGIN
if(g_ascii_strcasecmp(board->type, "menu") == 0)
{
GooCanvasItem *item = goo_canvas_svg_new (parent,
gc_skin_rsvg_get(),
"svg-id", "#MENUICON",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
SET_ITEM_LOCATION(item,
current_x - pixmap_w/2 - 25,
current_y - pixmap_h/2);
}
else
{
// display difficulty stars
if (board->difficulty != NULL)
{
difficulty = atoi(board->difficulty);
menu_difficulty_display(parent,
(double)current_x - pixmap_w/2 - 25,
(double)current_y - pixmap_h/2,
(double) 0.6,
difficulty);
}
if ( gc_board_is_demo_only(board) )
menu_demo_display(parent,
(gdouble)(current_x - pixmap_w/2 - 20),
(gdouble)(current_y - pixmap_h/2 + 60) );
}
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(menu_pixmap);
#else
g_object_unref(menu_pixmap);
#endif
// display menu icon ========================== END
/*
* Now everything ready, map the events
* -------------------------------------
*/
g_object_set_data (G_OBJECT (menu_button), "board", board);
g_signal_connect(menu_button, "button_press_event",
(GCallback) item_event,
menuitems);
g_signal_connect (menu_button, "enter_notify_event",
(GCallback) on_enter_notify, menuitems);
g_signal_connect (menu_button, "leave_notify_event",
(GCallback) on_leave_notify, menuitems);
gc_item_focus_init(menu_button, NULL);
}
static gboolean
on_enter_notify (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventCrossing *event,
MenuItems *menuitems)
{
GcomprisBoard *board;
board = g_object_get_data (G_OBJECT (item), "board");
if(G_IS_OBJECT(menuitems->bg))
g_object_set(menuitems->bg,
"visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
if(board->title && G_IS_OBJECT(menuitems->boardname_item))
g_object_set (menuitems->boardname_item,
"text", board->title,
NULL);
if(board->description
&& G_IS_OBJECT(menuitems->description_item))
g_object_set(menuitems->description_item,
"text", board->description,
NULL);
if(board->author && G_IS_OBJECT(menuitems->author_item))
g_object_set (menuitems->author_item,
"text", board->author,
NULL);
return FALSE;
}
static gboolean
on_leave_notify (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventCrossing *event,
MenuItems *menuitems)
{
g_object_set(menuitems->bg,
"visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
g_object_set (menuitems->boardname_item,
"text", "", NULL);
g_object_set (menuitems->description_item,
"text", "", NULL);
g_object_set (menuitems->author_item,
"text", "", NULL);
return FALSE;
}
static gint
item_event(GooCanvasItem *item, GdkEvent *event, MenuItems *menuitems)
{
GcomprisBoard *board;
if(board_paused)
return FALSE;
if(boardRootItem == NULL)
return FALSE;
board = g_object_get_data (G_OBJECT (item), "board");
if(!menu_displayed)
return TRUE;
if (strcmp(board->type,"menu")==0)
{
gchar *path = g_strdup_printf("%s/%s",board->section, board->name);
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
display_section(path);
if (menu_position)
g_free(menu_position);
menu_position = path;
}
else if ( ! gc_board_is_demo_only(board) )
{
gc_sound_play_ogg ("sounds/level.wav", NULL);
gc_board_run_next (board);
}
return TRUE;
}
/** \brief create the area in which we display the board title and description
*
*/
static void
create_info_area(GooCanvasItem *parent, MenuItems *menuitems)
{
gint x = (double) info_x + info_w/2.0;
gint y = info_y;
g_assert(parent);
/* The description background */
menuitems->bg =
goo_canvas_rect_new (parent,
info_x,
info_y,
info_w,
info_h,
"stroke_color_rgba", 0xFFFFFFFFL,
"fill_color_rgba",
gc_skin_get_color("menu/description_bg_color"),
"line-width", (double) 2,
"radius-x", (double) 10,
"radius-y", (double) 10,
NULL);
g_object_set(menuitems->bg,
"visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
menuitems->boardname_item = \
goo_canvas_text_new (parent,
"",
x,
y + 20,
-1,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_big,
"fill-color-rgba", gc_skin_get_color("menu/text"),
"alignment", PANGO_ALIGN_CENTER,
NULL);
menuitems->description_item = \
goo_canvas_text_new (parent,
"",
x,
y + 70,
info_w - 10,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_medium,
"fill-color-rgba", gc_skin_get_color("menu/text"),
"alignment", PANGO_ALIGN_CENTER,
NULL);
menuitems->author_item = \
goo_canvas_text_new (parent,
"",
x,
y + 110,
-1,
GTK_ANCHOR_CENTER,
"font", gc_skin_font_board_tiny,
"fill-color-rgba", gc_skin_get_color("menu/text"),
"alignment", PANGO_ALIGN_CENTER,
NULL);
}
static gdouble
get_ratio(GdkPixbuf *pixmap, gdouble size)
{
gdouble ratio = 1.0;
gint pixmap_h, pixmap_w;
pixmap_w = gdk_pixbuf_get_width(pixmap);
pixmap_h = gdk_pixbuf_get_height(pixmap);
if (pixmap_h <= pixmap_w){
if (pixmap_w > size)
ratio = size / pixmap_w;
}
else {
if (pixmap_h > size)
ratio = size / pixmap_h;
}
return ratio;
}
static void
create_top(GooCanvasItem *parent, gchar *path)
{
gchar **splitted_section;
gint i = 1;
GdkPixbuf *pixmap = NULL;
gdouble ratio;
GcomprisBoard *board;
gchar *path1, *path2;
GooCanvasItem *item;
GcomprisProperties *properties = gc_prop_get();
if (!path)
return;
splitted_section = g_strsplit (path, "/", 0);
path1 = g_strdup("");
/* splitted_section[0] is always "" */
i = 1;
while (splitted_section[i] != NULL)
{
path2 = g_strdup_printf("%s/%s", path1, splitted_section[i]);
g_free(path1);
path1 = path2;
if (strcmp(path1, properties->root_menu)<0){
i++;
continue;
}
if (current_top_x == 0.0)
{
current_top_x = top_x;
current_top_y = top_y;
}
else
{
item = \
goo_canvas_svg_new (parent,
gc_skin_rsvg_get(),
"svg-id", "#MENUICON",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
SET_ITEM_LOCATION(item,
current_top_x,
current_top_y + top_arrow_size);
// goo_canvas_item_scale(item, ratio, ratio);
current_top_x += top_arrow_size + top_int_x;
}
board = gc_menu_section_get(path1);
pixmap = gc_pixmap_load(board->icon_name);
ratio = get_ratio( pixmap, icon_size_top);
item = goo_canvas_image_new (parent,
pixmap,
0, 0,
NULL);
goo_canvas_item_translate(item,
current_top_x,
current_top_y);
goo_canvas_item_scale(item, ratio, ratio);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
current_top_x += top_int_x + icon_size_top;
g_object_set_data (G_OBJECT (item), "board", board);
g_signal_connect(item, "button_press_event",
(GCallback) item_event,
menuitems);
gc_item_focus_init(item, NULL);
i++;
}
g_strfreev(splitted_section);
g_free(path1);
}
static gboolean
display_welcome_event (GooCanvasItem *item,
GooCanvasItem *target,
GdkEventCrossing *event,
MenuItems *menuitems)
{
if(G_IS_OBJECT(menuitems->bg))
g_object_set(menuitems->bg,
"visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
if(G_IS_OBJECT(menuitems->boardname_item))
g_object_set(menuitems->boardname_item,
"text", "GCompris V" VERSION,
NULL);
if(G_IS_OBJECT(menuitems->description_item))
g_object_set(menuitems->description_item,
"text",
_("GCompris is a collection of educational games that provides different activities for children aged 2 and up."),
NULL);
if(G_IS_OBJECT(menuitems->author_item))
g_object_set(menuitems->author_item,
"text", "", NULL);
return FALSE;
}
static void
display_welcome (MenuItems *menuitems)
{
GooCanvasItem *item;
RsvgHandle *svg_handle;
if (actualSectionItem)
g_error("actualSectionItem exists in display_section !");
actualSectionItem = goo_canvas_group_new(boardRootItem,
NULL);
svg_handle = gc_skin_rsvg_get();
item = goo_canvas_svg_new (actualSectionItem,
svg_handle,
"svg-id", "#LOGO",
NULL);
SET_ITEM_LOCATION_CENTER(item,
display_x + display_w/2.0,
display_y + display_h/3.0)
g_signal_connect (item, "enter_notify_event",
(GCallback) display_welcome_event, menuitems);
g_signal_connect (item, "leave_notify_event",
(GCallback) on_leave_notify, menuitems);
menu_displayed = TRUE;
}
static void
menu_config_start(GcomprisBoard *agcomprisBoard,
GcomprisProfile *aProfile){
if(gcomprisBoard!=NULL)
{
menu_pause(TRUE);
gc_config_start();
}
}
/* ======================= */
/* = config_stop = */
/* ======================= */
static void
menu_config_stop()
{
}
/**
* Display the number of stars representing the difficulty level at the x,y location
* The stars are created in a group 'parent'
* The new group in which the stars are created is returned.
*/
static GooCanvasItem *
menu_difficulty_display(GooCanvasItem *parent,
double x, double y,
double ratio,
gint difficulty)
{
GooCanvasItem *stars_group = NULL;
GooCanvasItem *item = NULL;
gchar *svg_id = NULL;
if(difficulty==0 || difficulty>6)
return NULL;
svg_id = g_strdup_printf("#DIFFICULTY%d", difficulty);
stars_group = goo_canvas_group_new (parent, NULL);
item = goo_canvas_svg_new (stars_group,
gc_skin_rsvg_get(),
"svg-id", svg_id,
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
SET_ITEM_LOCATION(item, x, y);
g_free(svg_id);
return(stars_group);
}
/**
* Display the demo only icon
*/
static GooCanvasItem *
menu_demo_display(GooCanvasItem *parent,
gdouble x, gdouble y)
{
GooCanvasItem *item = goo_canvas_svg_new (parent,
gc_skin_rsvg_get(),
"svg-id", "#UNCHECKED",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
SET_ITEM_LOCATION(item, x, y);
return(item);
}