/* gcompris - shapegame.c
*
* Copyright (C) 2000, 2008 Bruno Coudoin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, see .
*/
/* libxml includes */
#include
#include
#include
#include
#include "gcompris/gcompris.h"
#include "gcompris/pixbuf_util.h"
#define SOUNDLISTFILE PACKAGE
#define SQUARE_LIMIT_DISTANCE 100.0
static int gamewon;
static GcomprisBoard *gcomprisBoard = NULL;
static gboolean board_paused = TRUE;
static gboolean shadow_enable;
#define POINT_COLOR_OFF 0xEf000080
#define POINT_COLOR_ON 0x00EF0080
typedef enum
{
SHAPE_TARGET = 1 << 0,
SHAPE_DUMMY_TARGET = 1 << 1,
SHAPE_ICON = 1 << 2,
SHAPE_BACKGROUND = 1 << 3,
} ShapeType;
/* Let's define the structure for a single shape */
typedef struct _Shape Shape;
struct _Shape {
char *name; /* name of the shape */
char *tooltip; /* optional tooltip for the shape */
char *pixmapfile; /* relative pixmap file name of the shape */
char *targetfile; /* OPTIONAL relative pixmap file name of the target shape, by default
a red point is displayed */
double x; /* x coordinate */
double y; /* y coordinate */
double w; /* width */
double h; /* height */
double zoomx; /* x zoom factor */
double zoomy; /* y zoom factor */
gint position; /* depth position 0=bottom other=intermediate */
char *soundfile; /* relative sound file to be played when pressing mouse button */
ShapeType type; /* Type of shape */
GooCanvasItem *item; /* Canvas item for this shape */
/* Root index which this item is in the shapelist */
GdkPixbuf *pixmap; /* The pixmap of the target item */
guint shapelistgroup_index; /* Root index which this item is in the shapelist */
Shape *icon_shape; /* Temporary Canvas icon shape for this shape */
Shape *target_shape; /* If this is an icon shape then point to its shape */
GooCanvasItem *target_point; /* Target point item for this shape */
GooCanvasItem *targetitem; /* Target item for this shape (if targetfile is defined) */
double offset_x, offset_y;
Shape *shape_place; /* the shape place in this place */
Shape *placed ; /* where is placed this shape */
GooCanvasItem *crossitem; /* A red cross indicating the item is */
/* not at the correct position */
};
/* This is the list of shape for the current game */
static GList *shape_list = NULL;
static GList *shape_list_group = NULL;
static int current_shapelistgroup_index = -1;
static GooCanvasItem *next_shapelist_item = NULL; /* Canvas item button for the next shapelist */
static GooCanvasItem *previous_shapelist_item = NULL; /* Canvas item button for the previous shapelist */
/* Let's define the structure for the shapebox list that contains the icons of the shapes */
typedef struct _ShapeBox ShapeBox;
struct _ShapeBox {
double x; /* x coordinate */
double y; /* y coordinate */
double w; /* width */
double h; /* height */
guint nb_shape_x; /* Number of shape on x */
guint nb_shape_y; /* Number of shape on y */
};
static ShapeBox shapeBox;
#define BUTTON_SPACE 40
/* Hash table of all the shapes in the list of shapes (one by different pixmap plus icon list) */
static GHashTable *shapelist_table = NULL;
static gint SHAPE_BOX_WIDTH_RATIO = 18;
static GooCanvasItem *shape_root_item;
static GooCanvasItem *title_root_item;
/* info is like title but only displayed when the puzzle
is completed */
static GooCanvasItem *info_root_item;
static GooCanvasItem *shape_list_root_item;
/* The tooltip */
static GooCanvasItem *tooltip_root_item;
static GooCanvasItem *tooltip_text_item;
static GooCanvasItem *tooltip_bg_item;
/* The continue button */
static GooCanvasItem *continue_root_item;
static GooCanvasItem *selector_item;
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 void process_ok(void);
static void process_nok(void);
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str);
static void config_start (GcomprisBoard *agcomprisBoard,
GcomprisProfile *aProfile);
static void config_stop (void);
static void shapegame_init_canvas(GooCanvasItem *parent);
static void shapegame_destroy_all_items(void);
static void setup_item(GooCanvasItem *item, Shape *shape);
static void shapegame_next_level(void);
static gboolean read_xml_file(char *fname);
static Shape *find_closest_shape(double x, double y, double limit);
static Shape *create_shape(ShapeType type, char *name, char *tooltip,
char *pixmapfile,
char *targetfile, double x, double y,
double w, double h, double zoomx,
double zoomy, guint position, char *soundfile);
static gboolean increment_sublevel(void);
static void create_title(GooCanvasItem *parent,
char *name, double x, double y,
GtkAnchorType anchor,
guint32 color_rgba,
gchar *color_background);
static gint item_event_ok(GooCanvasItem *item, GooCanvasItem *target,
GdkEvent *event,
gchar *data);
static gint item_event_drag(GooCanvasItem *item, GooCanvasItem *target,
GdkEvent *event, Shape *shape);
#if DEBUG
static void dump_shapes(void);
static void dump_shape(Shape *shape);
#endif
static void update_shapelist_item(void);
static void auto_process(void);
static void show_errors(void);
static void hide_errors(void);
static gint drag_mode;
/* Description of this plugin */
static BoardPlugin menu_bp =
{
NULL,
NULL,
"Make the puzzle",
"Drag and Drop the items to rebuild the object",
"Bruno Coudoin ",
NULL,
NULL,
NULL,
NULL,
start_board,
pause_board,
end_board,
is_our_board,
key_press,
NULL,
set_level,
NULL,
NULL,
config_start,
config_stop
};
/*
* Main entry point mandatory for each Gcompris's game
* ---------------------------------------------------
*
*/
GET_BPLUGIN_INFO(shapegame)
/*
* in : boolean TRUE = PAUSE : FALSE = CONTINUE
*
*/
static void pause_board (gboolean pause)
{
if(gcomprisBoard==NULL)
return;
if(gamewon == TRUE && pause == FALSE) /* the game is won */
{
if(increment_sublevel())
shapegame_next_level();
}
board_paused = pause;
}
/*
*/
static void start_board (GcomprisBoard *agcomprisBoard)
{
gchar *filename = NULL;
gboolean default_background = TRUE;
GHashTable *config = gc_db_get_board_conf();
if (strcmp(agcomprisBoard->name, "imagename")==0){
gc_locale_set(g_hash_table_lookup( config, "locale"));
}
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
if (drag_mode_str && (strcmp (drag_mode_str, "NULL") != 0))
drag_mode = g_ascii_strtod(drag_mode_str, NULL);
else
drag_mode = 0;
g_hash_table_destroy(config);
if(agcomprisBoard!=NULL)
{
gcomprisBoard = agcomprisBoard;
/* disable im_context */
gcomprisBoard->disable_im_context = TRUE;
/* set initial values for this level */
gcomprisBoard->level = 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_0.xml",
gcomprisBoard->boarddir,
gcomprisBoard->maxlevel,
NULL)) )
{
gcomprisBoard->maxlevel++;
g_free(filename);
}
g_free(filename);
gcomprisBoard->maxlevel--;
gc_bar_set(GC_BAR_CONFIG|GC_BAR_LEVEL);
gc_bar_location(10, -1, 0.6);
gcomprisBoard->sublevel = 0;
/* In this board, the sublevels are dynamicaly discovered based on data files */
gcomprisBoard->number_of_sublevel=G_MAXINT;
if(gcomprisBoard->mode!=NULL)
if(g_ascii_strncasecmp(gcomprisBoard->mode, "background=", 11)==0)
{
gchar *tmp = NULL;
tmp = g_malloc(strlen(gcomprisBoard->mode));
tmp = strcpy(tmp, gcomprisBoard->mode + 11);
gc_set_background(goo_canvas_get_root_item(gcomprisBoard->canvas), tmp);
default_background = FALSE;
g_free(tmp);
}
if(default_background)
{
// Default case, load the default background
gc_set_default_background(goo_canvas_get_root_item(gcomprisBoard->canvas));
}
// And the vertical selector
selector_item =
goo_canvas_svg_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
gc_skin_rsvg_get(),
"svg-id", "#SELECTOR",
"pointer-events", GOO_CANVAS_EVENTS_NONE,
NULL);
/* FIXME: This no more works ! */
gc_drag_start(goo_canvas_get_root_item(gcomprisBoard->canvas),
(GcDragFunc) item_event_drag, drag_mode);
shapegame_next_level();
pause_board(FALSE);
}
}
static void
end_board ()
{
if(gcomprisBoard!=NULL)
{
gc_drag_stop(goo_canvas_get_root_item(gcomprisBoard->canvas));
pause_board(TRUE);
shapegame_destroy_all_items();
gcomprisBoard->level = 1; // Restart this game to zero
}
if (strcmp(gcomprisBoard->name, "imagename")==0){
gc_locale_set( NULL );
}
if (selector_item)
goo_canvas_item_remove(selector_item);
selector_item = NULL;
gcomprisBoard = NULL;
}
static void
set_level (guint level)
{
if(gcomprisBoard!=NULL)
{
gcomprisBoard->level=level;
gcomprisBoard->sublevel=0;
shapegame_next_level();
}
}
gboolean
is_our_board (GcomprisBoard *gcomprisBoard)
{
if (gcomprisBoard)
{
if(g_ascii_strcasecmp(gcomprisBoard->type, "shapegame")==0)
{
gcomprisBoard->plugin = &menu_bp;
return TRUE;
}
}
return FALSE;
}
/*
* Keypress here are use for entering the editing mode
*/
static gint key_press(guint keyval, gchar *commit_str, gchar *preedit_str)
{
if(!gcomprisBoard)
return FALSE;
/* Add some filter for control and shift key */
switch (keyval)
{
/* Avoid all this keys to be interpreted by this game */
case GDK_Shift_L:
case GDK_Shift_R:
case GDK_Control_L:
case GDK_Control_R:
case GDK_Caps_Lock:
case GDK_Shift_Lock:
case GDK_Meta_L:
case GDK_Meta_R:
case GDK_Alt_L:
case GDK_Alt_R:
case GDK_Super_L:
case GDK_Super_R:
case GDK_Hyper_L:
case GDK_Hyper_R:
case GDK_Num_Lock:
return FALSE;
case GDK_KP_Enter:
case GDK_Return:
return FALSE;
case GDK_Right:
case GDK_Delete:
case GDK_BackSpace:
case GDK_Left:
break;
}
return TRUE;
}
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------*/
/*
* Returns true if increment is done, false is end of board
*/
static gboolean increment_sublevel()
{
gcomprisBoard->sublevel++;
if(gcomprisBoard->sublevel>gcomprisBoard->number_of_sublevel) {
/* Try the next level */
gcomprisBoard->level++;
gcomprisBoard->sublevel=0;
if(gcomprisBoard->level>gcomprisBoard->maxlevel) { // the current board is finished : restart
gcomprisBoard->level = 1;
gcomprisBoard->sublevel=0;
}
}
return TRUE;
}
/* set initial values for the next level */
static void shapegame_next_level()
{
char *filename;
gamewon = FALSE;
gc_drag_stop(goo_canvas_get_root_item(gcomprisBoard->canvas));
shapegame_destroy_all_items();
next_shapelist_item = previous_shapelist_item = NULL;
shapegame_init_canvas(goo_canvas_get_root_item(gcomprisBoard->canvas));
while( ((filename = gc_file_find_absolute("%s/board%d_%d.xml",
gcomprisBoard->boarddir,
gcomprisBoard->level,
gcomprisBoard->sublevel,
NULL)) == NULL)
&& ((gcomprisBoard->level != 1) || (gcomprisBoard->sublevel!=0)))
{
/* Try the next level */
gcomprisBoard->sublevel = gcomprisBoard->number_of_sublevel;
if(!increment_sublevel())
{
g_free(filename);
return;
}
}
gc_bar_set_level(gcomprisBoard);
read_xml_file(filename);
gc_drag_start(goo_canvas_get_root_item(gcomprisBoard->canvas),
(GcDragFunc) item_event_drag, drag_mode);
g_free(filename);
}
static void process_ok()
{
gamewon = TRUE;
/* Show the tooltip to let the user continue the game */
g_object_set (continue_root_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
}
static void process_nok()
{
gamewon = FALSE;
/* Show the tooltip to let the user continue the game */
g_object_set (continue_root_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
}
static void destroy_shape (Shape *shape)
{
g_free(shape->name);
g_free(shape->pixmapfile);
g_free(shape->targetfile);
g_free(shape->soundfile);
g_free(shape->tooltip);
if(shape->pixmap) {
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(shape->pixmap);
#else
g_object_unref(shape->pixmap);
#endif
}
g_free(shape);
}
/* Destroy all the items */
static void shapegame_destroy_all_items()
{
Shape *shape;
/* Cleanup of the shapes */
while(g_list_length(shape_list)>0)
{
shape = g_list_nth_data(shape_list, 0);
shape_list = g_list_remove (shape_list, shape);
destroy_shape(shape);
}
g_list_free(shape_list);
if (shapelist_table)
{
/* Deleting the root item automatically deletes children items */
goo_canvas_item_remove(shape_list_root_item);
shape_list_root_item = NULL;
goo_canvas_item_remove(shape_root_item);
shape_root_item = NULL;
title_root_item = NULL;
info_root_item = NULL;
goo_canvas_item_remove(tooltip_root_item);
tooltip_root_item = NULL;
gc_item_focus_remove(continue_root_item, NULL);
goo_canvas_item_remove(continue_root_item);
continue_root_item = NULL;
g_hash_table_destroy (shapelist_table);
shapelist_table=NULL;
g_list_free(shape_list_group);
shape_list_group = NULL;
current_shapelistgroup_index = -1;
}
}
static void shapegame_init_canvas(GooCanvasItem *parent)
{
shape_root_item = goo_canvas_group_new (parent, NULL);
goo_canvas_item_translate(shape_root_item,
BOARDWIDTH/SHAPE_BOX_WIDTH_RATIO,
0);
title_root_item = goo_canvas_group_new (shape_root_item, NULL);
info_root_item = goo_canvas_group_new (shape_root_item, NULL);
g_object_set (info_root_item, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
shape_list_root_item = goo_canvas_group_new (parent, NULL);
/* Create the tooltip area */
tooltip_root_item = goo_canvas_group_new (parent,
NULL);
goo_canvas_item_translate(tooltip_root_item, 10, BOARDHEIGHT-70);
tooltip_bg_item = \
goo_canvas_rect_new (tooltip_root_item,
0,
0,
0,
0,
"stroke_color_rgba", 0xFFFFFFFFL,
"fill_color_rgba", 0x0000FF90L,
"line-width", (double) 2,
"radius-x", (double) 10,
"radius-y", (double) 10,
NULL);
tooltip_text_item = \
goo_canvas_text_new (tooltip_root_item,
"",
15,
15,
-1,
GTK_ANCHOR_WEST,
"font", gc_skin_font_board_small,
"fill_color_rgba", gc_skin_color_text_button,
NULL);
/* Hide the tooltip */
g_object_set (tooltip_root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
/* Create the continue button */
continue_root_item = goo_canvas_group_new (goo_canvas_get_root_item(gcomprisBoard->canvas),
NULL);
goo_canvas_item_translate(continue_root_item, 5, 5);
continue_root_item = \
goo_canvas_svg_new (continue_root_item,
gc_skin_rsvg_get(),
"svg-id", "#OK",
NULL);
SET_ITEM_LOCATION(continue_root_item, 15, 15);
gc_item_focus_init(continue_root_item, NULL);
g_signal_connect(continue_root_item,
"button_press_event",
(GCallback) item_event_ok,
"continue_click");
g_signal_connect(continue_root_item,
"enter_notify_event",
(GCallback) item_event_ok,
"title_raise");
g_signal_connect(continue_root_item,
"leave_notify_event",
(GCallback) item_event_ok,
"title_lower");
/* Hide the continue */
g_object_set (continue_root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
/**************************************************************
* Shape list management
*/
/* Add the given shape to the list of shapes
* Do nothing if the shape is already in
*/
static void
add_shape_to_list_of_shapes(Shape *shape)
{
GooCanvasItem *item;
GooCanvasItem *shape_list_group_root = NULL;
double ICON_GAP = 5.0;
double ICON_HEIGHT = (double)(shapeBox.h / shapeBox.nb_shape_y) - ICON_GAP;
double ICON_WIDTH = (double)(shapeBox.w / shapeBox.nb_shape_x) - ICON_GAP;
if(!shapelist_table)
shapelist_table= g_hash_table_new (g_str_hash, g_str_equal);
/*----------------------------------------------------------------------*/
/* If the first list is full, add the previous/forward buttons */
if(g_hash_table_size(shapelist_table) == (shapeBox.nb_shape_x * shapeBox.nb_shape_y))
{
previous_shapelist_item = \
goo_canvas_svg_new (shape_list_root_item,
gc_skin_rsvg_get(),
"svg-id", "#PREVIOUS",
NULL);
SET_ITEM_LOCATION(previous_shapelist_item,
shapeBox.x - 5,
shapeBox.h);
g_signal_connect(previous_shapelist_item,
"button_press_event",
(GCallback) item_event_ok,
"previous_shapelist");
gc_item_focus_init(previous_shapelist_item, NULL);
next_shapelist_item = \
goo_canvas_svg_new (shape_list_root_item,
gc_skin_rsvg_get(),
"svg-id", "#NEXT",
NULL);
SET_ITEM_LOCATION(next_shapelist_item,
shapeBox.x + shapeBox.w / 2,
shapeBox.h);
g_signal_connect(next_shapelist_item, "button_press_event",
(GCallback) item_event_ok,
"next_shapelist");
gc_item_focus_init(next_shapelist_item, NULL);
g_object_set (next_shapelist_item, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
/*----------------------------------------------------------------------*/
/* Do We need to create a new list */
if(g_hash_table_size(shapelist_table)%(shapeBox.nb_shape_x * shapeBox.nb_shape_y)==0)
{
current_shapelistgroup_index++;
// Hide the previous group
if(current_shapelistgroup_index>=1)
{
shape_list_group_root = GOO_CANVAS_ITEM(g_list_nth_data(shape_list_group,
current_shapelistgroup_index-1));
//g_object_set (shape_list_group_root, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
item = g_list_nth_data(shape_list_group, current_shapelistgroup_index-1);
g_object_set (item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
// We need to start a new shape list group
shape_list_group_root = \
goo_canvas_group_new (shape_list_root_item,
NULL);
shape_list_group = g_list_append (shape_list_group, shape_list_group_root);
}
else
{
// Get the current shapelist group
shape_list_group_root = g_list_nth_data(shape_list_group,
current_shapelistgroup_index);
}
/*----------------------------------------------------------------------*/
/* This pixmap is not yet in the list of shapes */
if(g_hash_table_lookup (shapelist_table, shape->pixmapfile) == NULL)
{
double y_offset = 0;
double x_offset = 0;
GdkPixbuf *pixmap = NULL;
y_offset = shapeBox.y + (ICON_HEIGHT/2 +
(g_hash_table_size(shapelist_table) % shapeBox.nb_shape_y) *
ICON_HEIGHT);
x_offset = shapeBox.x + (ICON_WIDTH/2 +
((g_hash_table_size(shapelist_table) %
( shapeBox.nb_shape_x * shapeBox.nb_shape_y)) /
shapeBox.nb_shape_y) *
ICON_WIDTH);
/* So this shape is not yet in, let's put it in now */
g_hash_table_insert (shapelist_table, shape->pixmapfile, shape);
if(shape->pixmapfile)
{
pixmap = gc_pixmap_load(shape->pixmapfile);
if(pixmap)
{
double w, h, z;
Shape *icon_shape;
/* Calc a zoom factor so that the shape will fit in the shapelist
whatever its current size */
w = ICON_WIDTH;
h = gdk_pixbuf_get_height(pixmap) * (w / gdk_pixbuf_get_width(pixmap));
z = ICON_WIDTH / gdk_pixbuf_get_width(pixmap);
if(h > ICON_HEIGHT)
{
h = ICON_HEIGHT;
w = gdk_pixbuf_get_width(pixmap) * ( h / gdk_pixbuf_get_height(pixmap));
z = ICON_HEIGHT / gdk_pixbuf_get_height(pixmap);
}
if(h < 20 || w < 20)
{
GdkPixbuf *scale, *hand;
scale = gdk_pixbuf_scale_simple(pixmap, w, h, GDK_INTERP_BILINEAR);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
pixmap = gdk_pixbuf_new( GDK_COLORSPACE_RGB, TRUE, 8,
ICON_WIDTH, ICON_HEIGHT);
gdk_pixbuf_fill(pixmap, 0xffffff00);
// add the shape
gdk_pixbuf_copy_area( scale, 0, 0, w, h,
pixmap, (ICON_WIDTH-w )/2, (ICON_HEIGHT-h)/2 );
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(scale);
#else
g_object_unref(scale);
#endif
// add the hand
hand = gc_pixmap_load("shapegame/hand.svg");
h = ICON_HEIGHT/3;
w = gdk_pixbuf_get_width(hand) * h / gdk_pixbuf_get_height(hand);
scale = gdk_pixbuf_scale_simple(hand, w, h, GDK_INTERP_BILINEAR);
gdk_pixbuf_copy_area(scale, 0, 0, w, h,
pixmap, ICON_WIDTH-w,0);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(hand);
gdk_pixbuf_unref(scale);
#else
g_object_unref(hand);
g_object_unref(scale);
#endif
w = ICON_WIDTH;
h = ICON_HEIGHT;
z = 1;
}
item = goo_canvas_image_new (shape_list_group_root,
pixmap,
0,
0,
NULL);
goo_canvas_item_translate(item,
x_offset - w/2,
y_offset - h/2);
goo_canvas_item_scale(item, z, z);
g_object_set_data(G_OBJECT(item), "z", GINT_TO_POINTER((int)(z * 1000)));
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(pixmap);
#else
g_object_unref(pixmap);
#endif
icon_shape = create_shape(SHAPE_ICON, shape->name, shape->tooltip,
shape->pixmapfile, shape->targetfile,
(double)x_offset -w/2, (double)y_offset -h/2,
(double)w, (double)h,
(double)shape->zoomx, (double)shape->zoomy,
0, shape->soundfile);
icon_shape->item = item;
icon_shape->target_shape = shape;
shape->icon_shape = icon_shape;
icon_shape->shapelistgroup_index = current_shapelistgroup_index;
shape->shapelistgroup_index = current_shapelistgroup_index;
setup_item(item, icon_shape);
gc_item_focus_init(item, NULL);
}
}
}
}
/*
* Find the closest shape from the given point if it is located at
* a distance under the given square limit.
*/
static Shape *
find_closest_shape(double x, double y, double limit)
{
GList *list;
double goodDist = limit;
Shape *candidateShape = NULL;
/* loop through all our shapes */
for(list = shape_list; list != NULL; list = list->next)
{
Shape *shape = list->data;
double dist;
if(shape->type == SHAPE_TARGET)
{
/* Calc the distance between this shape and the given coord */
dist = sqrt(pow((shape->x-x),2) + pow((shape->y-y),2));
if(distnext)
{
Shape * s = list->data;
if(s->type == SHAPE_TARGET || s->type == SHAPE_ICON)
dump_shape(s);
}
}
static void
dump_shape(Shape *shape)
{
if(shape->type == SHAPE_TARGET && (shape->placed || shape->shape_place))
{
printf("%s :", shape->name);
if(shape->placed)
printf(" %s -> %s", shape->name, shape->placed->name);
else
printf(" ");
if(shape->shape_place)
printf(" %s -> %s", shape->shape_place->name, shape->name);
printf("\n");
}
}
#endif
/* it puts a shape back to the list of shapes */
static void
shape_goes_back_to_list(Shape *shape)
{
gdouble z;
g_object_set (continue_root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE,
NULL);
hide_errors();
if(shape->type == SHAPE_ICON)
shape = shape->target_shape;
g_object_set (shape->item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
/* replace the icon */
gc_item_absolute_move(shape->icon_shape->item,
shape->icon_shape->x,
shape->icon_shape->y);
z = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (shape->icon_shape->item),
"z"));
z = z / 1000;
goo_canvas_item_scale(shape->icon_shape->item, z, z);
g_object_set (shape->icon_shape->item, "visibility",
GOO_CANVAS_ITEM_VISIBLE, NULL);
if(shape->placed)
{
shape->placed->shape_place = NULL;
shape->placed = NULL;
}
update_shapelist_item();
gc_sound_play_ogg ("sounds/flip.wav", NULL);
}
/* switch off all point, and switch on this point
if shape is NULL, switch off all */
void
target_point_switch_on(Shape *shape_on)
{
GList *list;
Shape *shape;
for(list = shape_list; list ; list = list ->next)
{
shape = list -> data;
if(shape->type == SHAPE_TARGET && ! shape->targetfile)
g_object_set(shape->target_point,
"fill_color_rgba",
shape == shape_on ? POINT_COLOR_ON : POINT_COLOR_OFF,
NULL);
}
}
static gint
item_event_drag(GooCanvasItem *item,
GooCanvasItem *target,
GdkEvent *event, Shape *shape)
{
static GooCanvasItem *shadow_item = NULL;
static GooCanvasItem *dragged;
double item_x, item_y;
Shape *found_shape;
if(board_paused || shape == NULL)
return FALSE;
switch(event->type)
{
case GDK_BUTTON_PRESS:
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
switch(shape->type)
{
case SHAPE_TARGET:
/* unplace this shape */
if (shape->placed && shape->placed->target_point) {
g_object_set (shape->placed->target_point, "visibility",
GOO_CANVAS_ITEM_VISIBLE, NULL);
shape->placed->shape_place = NULL;
shape->placed = NULL;
}
/* No break on purpose */
case SHAPE_ICON:
gc_drag_offset_save(event);
gc_drag_offset_get(&shape->offset_x, &shape->offset_y);
if (shape->soundfile)
{
/* If the soundfile has space ' ' in it, then it is assumed that it is a list
* of sound rather than a single one */
char *p = NULL;
char *soundfile = g_strdup(shape->soundfile);
char *soundfiles = soundfile;
while ((p = strstr (soundfiles, " ")))
{
*p='\0';
gc_sound_play_ogg(soundfiles, NULL);
soundfiles = p + 1;
}
if (soundfiles != soundfile)
gc_sound_play_ogg(soundfiles, NULL);
else
gc_sound_play_ogg(soundfile, NULL);
g_free(soundfile);
}
break;
default:
break;
}
if(shadow_enable)
{
if(shadow_item)
goo_canvas_item_remove(shadow_item);
// initialise shadow shape
GdkPixbuf *dest;
dest = gdk_pixbuf_copy(shape->target_shape->pixmap);
pixbuf_add_transparent(dest, 100);
shadow_item = goo_canvas_image_new(shape_root_item,
dest,
0,
0,
NULL);
g_object_set(shadow_item, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
g_object_set(shadow_item, "pointer-events",
GOO_CANVAS_EVENTS_NONE, NULL);
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(dest);
#else
g_object_unref(dest);
#endif
}
dragged = shape->item;
gc_drag_item_move(event, NULL);
break;
case GDK_MOTION_NOTIFY:
if (item != dragged)
break;
gc_drag_item_move(event, NULL);
item_x = event->button.x;
item_y = event->button.y;
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
item, &item_x, &item_y);
found_shape = find_closest_shape(item_x - BOARDWIDTH/SHAPE_BOX_WIDTH_RATIO,
item_y, SQUARE_LIMIT_DISTANCE);
if(shadow_enable)
{
if(found_shape)
{
GooCanvasBounds bounds;
goo_canvas_item_get_bounds (shadow_item, &bounds);
gc_item_absolute_move(shadow_item,
found_shape->x - (bounds.x2 - bounds.x1) / 2
+ BOARDWIDTH/SHAPE_BOX_WIDTH_RATIO,
found_shape->y - (bounds.y2 - bounds.y1) / 2);
g_object_set (shadow_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
}
else
g_object_set (shadow_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
target_point_switch_on(found_shape);
break;
case GDK_BUTTON_RELEASE:
if (item != dragged)
break;
item_x = event->button.x;
item_y = event->button.y;
goo_canvas_convert_from_item_space(goo_canvas_item_get_canvas(item),
item, &item_x, &item_y);
if(shadow_enable && shadow_item)
{
goo_canvas_item_remove(shadow_item);
shadow_item = NULL;
}
target_point_switch_on(NULL);
found_shape = find_closest_shape(item_x - BOARDWIDTH/SHAPE_BOX_WIDTH_RATIO,
item_y,
SQUARE_LIMIT_DISTANCE);
if(found_shape)
{
GooCanvasBounds bounds;
GooCanvasItem *target_item;
target_item = shape->target_shape->item;
if(found_shape->shape_place)
shape_goes_back_to_list(found_shape->shape_place);
gc_sound_play_ogg ("sounds/line_end.wav", NULL);
/* place the target item */
goo_canvas_item_get_bounds(target_item, &bounds);
gc_item_absolute_move(target_item,
found_shape->x - (bounds.x2 - bounds.x1) / 2
+ BOARDWIDTH/SHAPE_BOX_WIDTH_RATIO,
found_shape->y - (bounds.y2 - bounds.y1) / 2);
if(found_shape->target_point)
g_object_set (found_shape->target_point, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
if(target_item)
{
g_object_set (target_item, "visibility",
GOO_CANVAS_ITEM_VISIBLE, NULL);
goo_canvas_item_raise(target_item, NULL);
}
if(shape->type == SHAPE_ICON)
g_object_set (shape->item, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
shape->target_shape->placed = found_shape;
found_shape->shape_place = shape->target_shape;
auto_process();
update_shapelist_item();
}
else
{
shape_goes_back_to_list(shape);
}
break;
default:
break;
}
return FALSE;
}
static gint
item_event(GooCanvasItem *item, GooCanvasItem *target,
GdkEvent *event, Shape *shape)
{
if(!gcomprisBoard || board_paused)
return FALSE;
if (shape == NULL )
return FALSE;
switch (event->type)
{
case GDK_ENTER_NOTIFY:
if(shape->tooltip) {
g_object_set(tooltip_text_item,
"text", gettext(shape->tooltip),
NULL);
g_object_set (tooltip_root_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
/* Set the background */
GooCanvasBounds bounds;
goo_canvas_item_get_bounds (tooltip_text_item, &bounds);
g_object_set(tooltip_bg_item,
"width", bounds.x2 - bounds.x1 + 30,
"height", bounds.y2 - bounds.y1 + 15,
NULL);
}
break;
case GDK_LEAVE_NOTIFY:
if(shape->tooltip)
g_object_set (tooltip_root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
break;
case GDK_BUTTON_PRESS:
if(event->button.button == 3)
shape_goes_back_to_list(shape);
default:
break;
}
return FALSE;
}
static int
get_element_count_listgroup(int listgroup_index)
{
int count=0, i;
Shape *sh;
for (i=0;ishapelistgroup_index == listgroup_index &&
sh->type == SHAPE_TARGET && ! sh->placed)
count ++;
}
return count;
}
static int
get_no_void_group(int direction)
{
int index = current_shapelistgroup_index;
direction = direction>0 ? 1 : -1;
index += direction;
while(0 <= index && index < g_list_length(shape_list_group))
{
if(get_element_count_listgroup(index))
return index;
index += direction;
}
return current_shapelistgroup_index;
}
static void
auto_process(void)
{
GList *list;
gboolean done = TRUE;
gboolean all_placed = TRUE;
/* Loop through all the shapes to find if all target are in place */
for(list = shape_list; list != NULL; list = list->next)
{
Shape *shape = list->data;
if(shape->type == SHAPE_TARGET)
{
if(shape->placed != shape)
done=FALSE;
if(shape->placed == NULL)
all_placed = FALSE;
}
}
if(done)
process_ok();
else if(all_placed)
process_nok();
}
static void
show_errors(void)
{
GList *list;
/* Loop through all the shapes to find and highlight the error item */
for(list = shape_list; list != NULL; list = list->next)
{
Shape *shape = list->data;
if(shape->type == SHAPE_TARGET)
{
if(shape->placed != shape)
{
g_object_set (shape->crossitem, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
goo_canvas_item_raise(shape->crossitem, NULL);
}
else
g_object_set (shape->crossitem, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
}
}
static void
hide_errors(void)
{
GList *list;
/* Loop through all the shapes and hide the error item */
for(list = shape_list; list != NULL; list = list->next)
{
Shape *shape = list->data;
if(shape->type == SHAPE_TARGET)
{
g_object_set (shape->crossitem, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
}
}
static void
update_shapelist_item(void)
{
if(! next_shapelist_item || !previous_shapelist_item)
return;
if(get_element_count_listgroup(current_shapelistgroup_index) ==0)
{
int index;
GooCanvasItem *root_item;
index = get_no_void_group(-1);
if(index == current_shapelistgroup_index)
index = get_no_void_group(1);
if(index != current_shapelistgroup_index)
{
root_item = g_list_nth_data(shape_list_group, current_shapelistgroup_index);
g_object_set (root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
root_item = g_list_nth_data(shape_list_group, index);
g_object_set (root_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
current_shapelistgroup_index = index;
}
}
if(get_no_void_group(1) == current_shapelistgroup_index)
g_object_set (next_shapelist_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
else
g_object_set (next_shapelist_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
if(get_no_void_group(-1) == current_shapelistgroup_index)
g_object_set (previous_shapelist_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
else
g_object_set (previous_shapelist_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
#if DEBUG
dump_shapes();
#endif
}
/* Callback for the previous / next shape operations */
static gint
item_event_ok(GooCanvasItem *item, GooCanvasItem *target,
GdkEvent *event, gchar *data)
{
GooCanvasItem *root_item;
if(board_paused)
return FALSE;
switch (event->type)
{
case GDK_BUTTON_PRESS:
gc_sound_play_ogg ("sounds/bleep.wav", NULL);
root_item = g_list_nth_data(shape_list_group, current_shapelistgroup_index);
g_object_set (root_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
if(!strcmp(data, "previous_shapelist"))
{
current_shapelistgroup_index = get_no_void_group(-1);
update_shapelist_item();
}
else if(!strcmp(data, "next_shapelist"))
{
current_shapelistgroup_index = get_no_void_group(1);
update_shapelist_item();
}
else if(!strcmp(data, "continue_click")) {
show_errors();
gc_bonus_display(gamewon, GC_BONUS_FLOWER);
}
root_item = g_list_nth_data(shape_list_group, current_shapelistgroup_index);
g_object_set (root_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
break;
case GDK_ENTER_NOTIFY:
if(!strcmp(data, "title_raise"))
{
goo_canvas_item_raise(title_root_item, NULL);
g_object_set (info_root_item, "visibility",
GOO_CANVAS_ITEM_VISIBLE, NULL);
goo_canvas_item_raise(info_root_item, NULL);
gc_bar_hide(TRUE);
}
case GDK_LEAVE_NOTIFY:
if(!strcmp(data, "title_lower"))
{
goo_canvas_item_lower(title_root_item, NULL);
g_object_set (info_root_item, "visibility",
GOO_CANVAS_ITEM_INVISIBLE, NULL);
gc_bar_hide(FALSE);
}
default:
break;
}
return FALSE;
}
static void
setup_item(GooCanvasItem *item, Shape *shape)
{
g_signal_connect(item, "enter_notify_event",
(GCallback) item_event,
shape);
g_signal_connect(item, "leave_notify_event",
(GCallback) item_event,
shape);
g_signal_connect(item, "button_press_event",
(GCallback) item_event,
shape);
g_signal_connect(item, "button_press_event",
(GCallback) gc_drag_event, shape);
g_signal_connect(item, "button_release_event",
(GCallback) gc_drag_event, shape);
}
/*
* Thanks for George Lebl for his Genealogy example
* for all the XML stuff there
*/
/* Adds a shape to the canvas */
static void
add_shape_to_canvas(Shape *shape)
{
GdkPixbuf *pixmap;
GdkPixbuf *targetpixmap;
GooCanvasItem *item = NULL;
g_return_if_fail(shape != NULL);
if(shape->type == SHAPE_TARGET)
{
if(shape->targetfile)
{
if(shape->targetfile[0] != '\0')
{
targetpixmap = gc_pixmap_load(shape->targetfile);
shape->w = (double)gdk_pixbuf_get_width(targetpixmap);
shape->h = (double)gdk_pixbuf_get_height(targetpixmap);
item = goo_canvas_image_new (shape_root_item,
targetpixmap,
0, 0,
NULL);
goo_canvas_item_translate(item,
shape->x - shape->w / 2,
shape->y - shape->h / 2);
goo_canvas_item_scale(item,
shape->zoomx, shape->zoomy);
shape->targetitem = item;
#if GDK_PIXBUF_MAJOR <= 2 && GDK_PIXBUF_MINOR <= 24
gdk_pixbuf_unref(targetpixmap);
#else
g_object_unref(targetpixmap);
#endif
}
// An empty targetfile means no target and no point
}
else
{
int point_size = 6;
/* Display a point to highlight the target location of this shape */
item = goo_canvas_ellipse_new (shape_root_item,
shape->x,
shape->y,
point_size,
point_size,
"fill_color_rgba", POINT_COLOR_OFF,
"stroke-color", "black",
"line-width", 2.0,
NULL);
shape->target_point = item;
pixmap = gc_pixmap_load("shapegame/error.svg");
if(pixmap)
{
/* Display the error cross */
shape->crossitem = \
goo_canvas_image_new (shape_root_item,
pixmap,
shape->x - gdk_pixbuf_get_width(pixmap) / 2,
shape->y - gdk_pixbuf_get_height(pixmap) / 2,
NULL);
g_object_set (shape->crossitem,
"visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
}
}
if (item)
goo_canvas_item_lower(item, NULL);
}
if(shape->pixmapfile)
{
pixmap = gc_pixmap_load(shape->pixmapfile);
if(pixmap)
{
shape->w = (double)gdk_pixbuf_get_width(pixmap);
shape->h = (double)gdk_pixbuf_get_height(pixmap);
/* Display the shape itself but hide it until the user puts the right shape on it */
/* I have to do it this way for the positionning (lower/raise) complexity */
item = goo_canvas_image_new (shape_root_item,
pixmap,
0,
0,
NULL);
goo_canvas_item_translate(item,
shape->x - shape->w / 2,
shape->y - shape->h / 2);
goo_canvas_item_scale(item, shape->zoomx, shape->zoomy);
shape->pixmap = pixmap;
}
}
/* Associate this item to this shape */
shape->item = item;
if(shape->type==SHAPE_TARGET || shape->type==SHAPE_DUMMY_TARGET)
{
setup_item(item, shape);
g_object_set (item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
add_shape_to_list_of_shapes(shape);
}
else if(shape->type==SHAPE_BACKGROUND)
{
goo_canvas_item_lower(item, NULL);
}
}
static void
create_title(GooCanvasItem *parent,
char *name, double x, double y,
GtkAnchorType anchor,
guint32 color_rgba,
gchar *color_background)
{
GooCanvasItem *item;
item = \
goo_canvas_text_new (parent,
gettext(name),
x,
y,
-1,
anchor,
"font", gc_skin_font_board_small,
"fill_color_rgba", color_rgba,
NULL);
/* Display a background if a color is provided */
if(color_background) {
guint32 color_background_rgba = gc_skin_get_color(color_background);
GooCanvasBounds bounds;
int gap = 8;
goo_canvas_item_get_bounds (item, &bounds);
goo_canvas_rect_new (parent,
x - (bounds.x2 - bounds.x1)/2 - gap,
y - (bounds.y2 - bounds.y1)/2 - gap,
bounds.x2 - bounds.x1 + gap*2,
bounds.y2 - bounds.y1 + gap*2,
"stroke_color_rgba", 0xFFFFFFFFL,
"fill_color_rgba", color_background_rgba,
"line-width", (double) 2,
"radius-x", (double) 10,
"radius-y", (double) 10,
NULL);
}
goo_canvas_item_raise(item, NULL);
}
static Shape *
create_shape(ShapeType type, char *name, char *tooltip, char *pixmapfile,
char *targetfile, double x, double y,
double w, double h, double zoomx,
double zoomy, guint position, char *soundfile)
{
Shape *shape;
/* allocate a new shape */
shape = g_new0(Shape,1);
shape->name = g_strdup(name);
if(tooltip)
shape->tooltip = g_strdup(tooltip);
else
shape->tooltip = NULL;
shape->pixmapfile = g_strdup(pixmapfile);
shape->targetfile = g_strdup(targetfile);
shape->x = x;
shape->y = y;
shape->w = w;
shape->h = h;
shape->zoomx = zoomx;
shape->zoomy = zoomy;
shape->position = position;
shape->type = type;
shape->soundfile = g_strdup(soundfile);
if(type != SHAPE_ICON)
shape->target_shape = shape;
/* add the shape to the list */
shape_list = g_list_append(shape_list, shape);
return shape;
}
/** return a double value from an nodePtr
*
* \param node
* \param prop
* \param def_value : default value
*
* \return a double value found in the 'prop' property of the 'node' or
* the default value if not found
*/
static double
xmlGetProp_Double(xmlNodePtr node, xmlChar *prop, double def_value)
{
double value;
char *str;
str = (char *)xmlGetProp(node, prop);
if(str)
{
value = g_ascii_strtod(str, NULL);
free(str);
}
else
value = def_value;
return value;
}
static void
add_xml_shape_to_data(xmlDocPtr doc, xmlNodePtr xmlnode, GNode * child, GList **list)
{
char *name,*ctype, *justification;
char *tooltip;
GtkAnchorType anchor_gtk;
char *pixmapfile = NULL;
char *targetfile = NULL;
char *soundfile = NULL;
double x, y, zoomx, zoomy;
guint position;
ShapeType type = SHAPE_TARGET;
Shape *shape;
xmlNodePtr xmlnamenode;
char *locale;
char *color_text;
guint color_rgba;
char *color_background = NULL;
if(/* if the node has no name */
!xmlnode->name ||
/* or if the name is not "Shape" */
((g_ascii_strcasecmp((const char *)xmlnode->name,"Shape")!=0) &&
/* or if the name is not "Title" */
(g_ascii_strcasecmp((const char *)xmlnode->name,"Title")!=0) &&
/* or if the name is not "Title" */
(g_ascii_strcasecmp((const char *)xmlnode->name,"Info")!=0) &&
/* or if the name is not "Option" */
(g_ascii_strcasecmp((const char *)xmlnode->name,"Option")!=0) )
)
return;
pixmapfile = (char *)xmlGetProp(xmlnode, BAD_CAST "pixmapfile");
targetfile = (char *)xmlGetProp(xmlnode, BAD_CAST "targetfile");
soundfile = (char *)xmlGetProp(xmlnode, BAD_CAST "sound");
/* get the X coord of the shape */
x = xmlGetProp_Double(xmlnode, BAD_CAST "x", 100);
/* get the Y coord of the shape */
y = xmlGetProp_Double(xmlnode, BAD_CAST "y", 100);
/* Back up the current locale to be sure to load well C formated numbers */
locale = g_strdup(gc_locale_get());
gc_locale_set("C");
/* get the ZOOMX coord of the shape */
zoomx = xmlGetProp_Double(xmlnode, BAD_CAST "zoomx", 1);
/* get the ZOOMY coord of the shape */
zoomy = xmlGetProp_Double(xmlnode, BAD_CAST "zoomy", 1);
/* get the POSITION of the shape : DEPRECATED */
/* Position in the xml means:
* 0 = BOTTOM
* 1 or more = TOP
*/
position = 0;
/* Back to the user locale */
gc_locale_set(locale);
g_free(locale);
/* get the TYPE of the shape */
ctype = (char *)xmlGetProp(xmlnode, BAD_CAST "type");
if(ctype) {
if(g_ascii_strcasecmp(ctype,"SHAPE_TARGET")==0)
type = SHAPE_TARGET;
else if(g_ascii_strcasecmp(ctype,"SHAPE_DUMMY_TARGET")==0)
type = SHAPE_DUMMY_TARGET;
else if (g_ascii_strcasecmp(ctype,"SHAPE_BACKGROUND")==0)
type = SHAPE_BACKGROUND;
xmlFree(ctype);
}
else
type = SHAPE_TARGET;
/* get the JUSTIFICATION of the Title */
anchor_gtk = GTK_ANCHOR_CENTER; /* GTK_ANCHOR_CENTER is default */
justification = (char *)xmlGetProp(xmlnode, BAD_CAST "justification");
if(justification) {
if (strcmp(justification, "GTK_JUSTIFY_LEFT") == 0) {
anchor_gtk = GTK_ANCHOR_WEST;
} else if (strcmp(justification, "GTK_JUSTIFY_RIGHT") == 0) {
anchor_gtk = GTK_ANCHOR_EAST;
} else if (strcmp(justification, "GTK_JUSTIFY_CENTER") == 0) {
anchor_gtk = GTK_ANCHOR_CENTER;
} else {
}
xmlFree(justification);
}
/* get the COLOR of the Title Specified by skin reference */
color_text = (char *)xmlGetProp(xmlnode, BAD_CAST "color_skin");
if(color_text) {
color_rgba = gc_skin_get_color(color_text);
xmlFree(color_text);
} else {
color_rgba = gc_skin_get_color("gcompris/content"); /* the default */
}
/* get the COLOR of the Title Specified by skin reference */
/* Default is NULL */
color_background = (char *)xmlGetProp(xmlnode, BAD_CAST "color_background_skin");
/* get the name and tooltip of the shape */
name = NULL;
tooltip = NULL;
xmlnamenode = xmlnode->xmlChildrenNode;
while (xmlnamenode != NULL) {
gchar *lang = (char *)xmlGetProp(xmlnamenode, BAD_CAST "lang");
/* get the name of the shape */
if (!strcmp((char *)xmlnamenode->name, "name")
&& (lang==NULL
|| !strcmp(lang, gc_locale_get())
|| !strncmp(lang, gc_locale_get(), 2)))
{
if (name)
xmlFree(name);
name = (char *)xmlNodeListGetString(doc, xmlnamenode->xmlChildrenNode, 1);
}
/* get the tooltip of the shape */
if (!strcmp((char *)xmlnamenode->name, "tooltip")
&& (lang==NULL
|| !strcmp(lang, gc_locale_get())
|| !strncmp(lang, gc_locale_get(), 2)))
{
if (tooltip)
xmlFree(tooltip);
tooltip = (char *)xmlNodeListGetString(doc, xmlnamenode->xmlChildrenNode, 1);
}
xmlFree(lang);
xmlnamenode = xmlnamenode->next;
}
/* If name is not given as an element, try to get it as a property */
if(!name)
name = (char *)xmlGetProp(xmlnode, BAD_CAST "name");
if(g_ascii_strcasecmp((char *)xmlnode->name, "Shape")==0)
{
/* add the shape to the database */
/* WARNING : I do not initialize the width and height since I don't need them */
shape = create_shape(type, name, tooltip, pixmapfile,
targetfile, x, y,
0, 0,
zoomx, zoomy, position,
soundfile);
/* add the shape to the list */
*list = g_list_append(*list, shape);
}
else if (g_ascii_strcasecmp((char *)xmlnode->name, "Title")==0)
{
/* Read \n is needed */
gchar *newname;
if(name != NULL) {
newname = g_strcompress(name);
create_title(title_root_item,
newname, x, y, anchor_gtk,
color_rgba, color_background);
g_free(newname);
}
}
else if (g_ascii_strcasecmp((char *)xmlnode->name, "Info")==0)
{
/* Read \n is needed */
gchar *newname;
if(name != NULL) {
newname = g_strcompress(name);
create_title(info_root_item,
newname, x, y, anchor_gtk,
color_rgba, color_background);
g_free(newname);
}
}
g_free(pixmapfile);
g_free(soundfile);
g_free(name);
g_free(targetfile);
g_free(tooltip);
xmlFree(color_background);
}
static void
insert_shape_random(GList *shapes_, int shapeMask)
{
int list_length, i;
GList *shapes = g_list_copy(shapes_);
/* Insert each of the shapes randomly */
while((list_length = g_list_length(shapes)))
{
Shape *shape;
i = g_random_int_range(0, list_length);
shape = g_list_nth_data(shapes, i);
if (shape->type & shapeMask)
add_shape_to_canvas(shape);
shapes = g_list_remove (shapes, shape);
}
g_list_free(shapes);
}
/* parse the doc, add it to our internal structures and to the clist */
static void
parse_doc(xmlDocPtr doc)
{
GList *shape_list_init = NULL;
xmlNodePtr node;
GooCanvasItem *item;
/* find nodes and add them to the list, this just
loops through all the children of the root of the document */
for(node = doc->children->children; node != NULL; node = node->next) {
/* add the shape to the list, there are no children so
we pass NULL as the node of the child */
add_xml_shape_to_data(doc, node, NULL, &shape_list_init);
}
shape_list = g_list_copy(shape_list_init);
insert_shape_random(shape_list_init, 0xFF ^ SHAPE_BACKGROUND);
insert_shape_random(shape_list_init, SHAPE_BACKGROUND);
g_list_free(shape_list_init);
shape_list_init = NULL;
if(current_shapelistgroup_index > 0)
{ /* If at least on shape group */
item = g_list_nth_data(shape_list_group, current_shapelistgroup_index);
g_object_set (item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
item = g_list_nth_data(shape_list_group, 0);
g_object_set (item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
g_object_set (previous_shapelist_item, "visibility", GOO_CANVAS_ITEM_INVISIBLE, NULL);
g_object_set (next_shapelist_item, "visibility", GOO_CANVAS_ITEM_VISIBLE, NULL);
current_shapelistgroup_index = 0;
}
}
/* 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)
{
/* pointer to the new doc */
xmlDocPtr doc;
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 ShapeGame node */
g_ascii_strcasecmp((char *)doc->children->name, "ShapeGame")!=0) {
xmlFreeDoc(doc);
return FALSE;
}
/*--------------------------------------------------*/
/* Read ShapeBox property */
shapeBox.x = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_x", 15);
shapeBox.y = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_y", 25);
shapeBox.w = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_w", 80);
shapeBox.h = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_h", 430);
shapeBox.nb_shape_x = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_nb_shape_x", 1);
shapeBox.nb_shape_y = xmlGetProp_Double(doc->children, BAD_CAST "shapebox_nb_shape_y", 5);
/* Read shadow enable property */
shadow_enable = xmlGetProp_Double(doc->children, BAD_CAST "shadow_enable", 1);
/* parse our document and replace old data */
parse_doc(doc);
xmlFreeDoc(doc);
return TRUE;
}
/* ************************************* */
/* * Configuration * */
/* ************************************* */
/* ======================= */
/* = config_start = */
/* ======================= */
static GcomprisProfile *profile_conf;
static GcomprisBoard *board_conf;
static void save_table (gpointer key,
gpointer value,
gpointer user_data)
{
gc_db_set_board_conf ( profile_conf,
board_conf,
(gchar *) key,
(gchar *) value);
}
static void conf_ok(GHashTable *table)
{
if (!table){
if (gcomprisBoard)
pause_board(FALSE);
return;
}
g_hash_table_foreach(table, (GHFunc) save_table, NULL);
if (gcomprisBoard){
GHashTable *config;
if (profile_conf)
config = gc_db_get_board_conf();
else
config = table;
if (strcmp(gcomprisBoard->name, "imagename")==0){
gc_locale_set(g_hash_table_lookup( config, "locale"));
}
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
if (drag_mode_str && (strcmp (drag_mode_str, "NULL") != 0))
drag_mode = (gint ) g_ascii_strtod(drag_mode_str, NULL);
else
drag_mode = 0;
if (profile_conf)
g_hash_table_destroy(config);
gc_drag_change_mode( drag_mode);
shapegame_next_level();
pause_board(FALSE);
}
board_conf = NULL;
profile_conf = NULL;
}
static void
config_start(GcomprisBoard *agcomprisBoard,
GcomprisProfile *aProfile)
{
board_conf = agcomprisBoard;
profile_conf = aProfile;
if (gcomprisBoard)
pause_board(TRUE);
gchar * label = g_strdup_printf(_("%1$s configuration\n for profile %2$s"),
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);
if (strcmp(agcomprisBoard->name, "imagename")==0){
gchar *locale = g_hash_table_lookup( config, "locale");
gc_board_config_combo_locales( bconf, locale);
}
gchar *drag_mode_str = g_hash_table_lookup( config, "drag_mode");
gint drag_previous;
if (drag_mode_str && (strcmp (drag_mode_str, "NULL") != 0))
drag_previous = (gint ) g_ascii_strtod(drag_mode_str, NULL);
else
drag_previous = 0;
gc_board_config_combo_drag(bconf, drag_previous);
}
/* ======================= */
/* = config_stop = */
/* ======================= */
static void
config_stop()
{
}