gcompris.c 48.2 KB
Newer Older
Bruno Coudoin's avatar
Bruno Coudoin committed
1 2
/* gcompris - gcompris.c
 *
3
 * Copyright (C) 2000, 2008 Bruno Coudoin
Bruno Coudoin's avatar
Bruno Coudoin committed
4
 *
5 6 7 8
 *   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.
Bruno Coudoin's avatar
Bruno Coudoin committed
9
 *
10 11 12 13
 *   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.
Bruno Coudoin's avatar
Bruno Coudoin committed
14
 *
15 16
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, see <http://www.gnu.org/licenses/>.
Bruno Coudoin's avatar
Bruno Coudoin committed
17 18
 */

19 20
#include <signal.h>

21 22 23 24
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
25
#include <string.h>
26

27 28 29 30 31
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#include <gdk/gdkx.h>

32 33
#include <glib/gstdio.h>

Bruno Coudoin's avatar
Bruno Coudoin committed
34
#include "gcompris.h"
35
#include "gc_core.h"
36
#include "gcompris_config.h"
37
#include "about.h"
38
#include <locale.h>
Bruno Coudoin's avatar
Bruno Coudoin committed
39

40 41
#include "binreloc.h"

Yves Combe's avatar
Yves Combe committed
42
/* for NSBUNDLE */
43
#ifdef NSBUNDLE
Yves Combe's avatar
Yves Combe committed
44
#include "gcompris-nsbundle.h"
45
#endif
Yves Combe's avatar
Yves Combe committed
46

47 48 49
/* get the default database name */
#define DEFAULT_DATABASE "gcompris_sqlite.db"

50 51
static double zoom_factor = 1.0;

52 53 54 55
/* Multiple instance check */
#define GC_LOCK_FILE "gcompris.lock"
#define GC_LOCK_LIMIT 30 /* seconds */

56
static GtkWidget *window;
57
static GtkWidget *canvas;
58
static GtkWidget *fixed;
59
static GtkWidget *drawing_area;
Yves Combe's avatar
Yves Combe committed
60 61
gchar * exec_prefix = NULL;

Bruno Coudoin's avatar
Bruno Coudoin committed
62 63
//static gint pause_board_cb (GtkWidget *widget, gpointer data);
static void quit_cb (GtkWidget *widget, gpointer data);
64
static void map_cb  (GtkWidget *widget, gpointer data);
65 66 67
static gint _gc_configure_event_callback (GtkWidget   *widget,
					  GdkEventConfigure *event,
					  gpointer     client_data);
68 69
static gboolean _expose_background_callback (GtkWidget *widget,
					     GdkEventExpose *event,
70
					     gpointer data);
71 72
static gboolean _realize_callback (GtkWidget *widget, GdkEventExpose *event,
				   gpointer data);
Bruno Coudoin's avatar
Bruno Coudoin committed
73 74 75
static gint board_widget_key_press_callback (GtkWidget   *widget,
					    GdkEventKey *event,
					    gpointer     client_data);
76
void gc_terminate(int signum);
77
static void single_instance_release();
Bruno Coudoin's avatar
Bruno Coudoin committed
78

79
/*
Bruno Coudoin's avatar
Bruno Coudoin committed
80
 * For the Activation dialog
81
 */
82
#ifdef STATIC_MODULE
Bruno Coudoin's avatar
Bruno Coudoin committed
83
int gc_activation_check(char *code);
84 85 86
static void activation_enter_callback(GtkWidget *widget,
				      GtkWidget *entry );
static void activation_done();
87
static int display_activation_dialog();
88
static GooCanvasItem *activation_item;
89 90
static GtkEntry *widget_activation_entry;
#else
91
#define display_activation_dialog() FALSE
92 93 94
#endif


95 96
static GcomprisProperties *properties = NULL;
static gboolean		   is_mapped = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
97
static gboolean		   fullscreen;
Bruno Coudoin's avatar
Bruno Coudoin committed
98 99 100 101

/****************************************************************************/
/* Some constants.  */

102
static GooCanvasItem *backgroundimg = NULL;
Bruno Coudoin's avatar
Bruno Coudoin committed
103
static GooCanvasItem *backgroundsvgimg = NULL;
104 105 106
static gchar           *gc_locale = NULL;
static gchar           *gc_user_default_locale = NULL;
static gboolean		gc_debug = FALSE;
Bruno Coudoin's avatar
Bruno Coudoin committed
107

108 109 110
/****************************************************************************/
/* Command line params */

111
/*** gcompris-popttable */
112 113 114 115 116 117 118 119
static gint popt_fullscreen	   = FALSE;
static gint popt_window		   = FALSE;
static gint popt_sound		   = FALSE;
static gint popt_mute		   = FALSE;
static gint popt_cursor		   = FALSE;
static gint popt_version	   = FALSE;
static gint popt_difficulty_filter = FALSE;
static gint popt_debug		   = FALSE;
120
static gint popt_nolockcheck	   = FALSE;
121
static gchar *popt_root_menu       = NULL;
122
static gchar *popt_package_data_dir = NULL;
123
static gchar *popt_package_skin_dir = NULL;
124 125
static gchar *popt_plugin_dir      = NULL;
static gchar *popt_python_plugin_dir = NULL;
126
static gchar *popt_locale_dir      = NULL;
127
static gchar *popt_menu_dir        = NULL;
128 129 130 131 132 133
static gint popt_administration	   = FALSE;
static gchar *popt_database        = NULL;
static gint popt_create_db   	   = FALSE;
static gint popt_reread_menu   	   = FALSE;
static gchar *popt_profile	   = NULL;
static gint *popt_profile_list	   = FALSE;
134 135
static gchar *popt_config_dir	   = NULL;
static gchar *popt_user_dir	   = NULL;
136 137 138
static gint  popt_experimental     = FALSE;
static gint  popt_no_quit	   = FALSE;
static gint  popt_no_config        = FALSE;
139 140 141
static gchar *popt_server          = NULL;
static gint  *popt_web_only        = NULL;
static gchar *popt_cache_dir       = NULL;
142
static gchar *popt_drag_mode       = NULL;
143 144
static gchar *sugarBundleId        = NULL;
static gchar *sugarActivityId      = NULL;
145 146 147

static GOptionEntry options[] = {
  {"fullscreen", 'f', 0, G_OPTION_ARG_NONE, &popt_fullscreen,
Bruno Coudoin's avatar
Bruno Coudoin committed
148
   N_("run GCompris in fullscreen mode."), NULL},
149

150
  {"window", 'w', 0, G_OPTION_ARG_NONE, &popt_window,
Bruno Coudoin's avatar
Bruno Coudoin committed
151
   N_("run GCompris in window mode."), NULL},
152

153
  {"sound", 's', 0, G_OPTION_ARG_NONE, &popt_sound,
Bruno Coudoin's avatar
Bruno Coudoin committed
154
   N_("run GCompris with sound enabled."), NULL},
155

156
  {"mute", 'm', 0, G_OPTION_ARG_NONE, &popt_mute,
Bruno Coudoin's avatar
Bruno Coudoin committed
157
   N_("run GCompris without sound."), NULL},
158

159
  {"cursor", 'c', 0, G_OPTION_ARG_NONE, &popt_cursor,
Bruno Coudoin's avatar
Bruno Coudoin committed
160
   N_("run GCompris with the default gnome cursor."), NULL},
161

162
  {"difficulty", 'd', 0, G_OPTION_ARG_INT, &popt_difficulty_filter,
163
   N_("display only activities with this difficulty level."), NULL},
164

165
  {"debug", 'D', 0, G_OPTION_ARG_NONE, &popt_debug,
166
   N_("display debug informations on the console."), NULL},
167

168
  {"version", 'v', 0, G_OPTION_ARG_NONE, &popt_version,
169
   N_("Print the version of " PACKAGE), NULL},
170

171
  {"root-menu", 'l', 0, G_OPTION_ARG_STRING, &popt_root_menu,
Bruno Coudoin's avatar
Bruno Coudoin committed
172 173 174
   N_("Run GCompris with local menu"
      " (e.g -l /reading will let you play only activities in the reading directory, -l /strategy/connect4 only the connect4 activity)."
      " Use '-l list' to list all the availaible activities and their descriptions."), NULL},
175

176 177 178
  {"package_data_dir", 'A', 0, G_OPTION_ARG_STRING, &popt_package_data_dir,
   N_("GCompris will find the data dir in this directory"), NULL},

179 180 181
  {"package_skin_dir", 'S', 0, G_OPTION_ARG_STRING, &popt_package_skin_dir,
   N_("GCompris will find the skins in this directory"), NULL},

182 183 184 185 186
  {"plugin_dir", 'L', 0, G_OPTION_ARG_STRING, &popt_plugin_dir,
   N_("GCompris will find the activity plugins in this directory"), NULL},

  {"python_plugin_dir", 'P', 0, G_OPTION_ARG_STRING, &popt_python_plugin_dir,
   N_("GCompris will find the python activity in this directory"), NULL},
187

188 189 190
  {"locale_dir", '\0', 0, G_OPTION_ARG_STRING, &popt_locale_dir,
   N_("GCompris will find the locale file (.mo translation) in this directory"), NULL},

191 192 193
  {"menu_dir", 'M', 0, G_OPTION_ARG_STRING, &popt_menu_dir,
   N_("GCompris will find the activities menu in this directory"), NULL},

194
  {"administration", 'a', 0, G_OPTION_ARG_NONE, &popt_administration,
195
   N_("Run GCompris in administration and user-management mode"), NULL},
196

197
  {"database", 'b', 0, G_OPTION_ARG_STRING, &popt_database,
198
   N_("Use alternate database for profiles [$HOME/.config/gcompris/gcompris_sqlite.db]"), NULL},
199

200
  {"create-db",'\0', 0, G_OPTION_ARG_NONE, &popt_create_db,
201
   N_("Create the alternate database for profiles"), NULL},
202

203
  {"reread-menu",'\0', 0, G_OPTION_ARG_NONE, &popt_reread_menu,
204
   N_("Re-read XML Menus and store them in the database"), NULL},
205

206
  {"profile",'p', 0, G_OPTION_ARG_STRING, &popt_profile,
207
   N_("Set the profile to use. Use 'gcompris -a' to create profiles"), NULL},
208

209
  {"profile-list",'\0', 0, G_OPTION_ARG_NONE, &popt_profile_list,
210
   N_("List all available profiles. Use 'gcompris -a' to create profiles"), NULL},
211

212
  {"config-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_config_dir,
213
   N_("Config directory location: [$HOME/.config/gcompris]. Alternate is to set $XDG_CONFIG_HOME."), NULL},
214

215
  {"user-dir",'\0', 0, G_OPTION_ARG_STRING, &popt_user_dir,
216
   N_("The location of user directories: [$HOME/My GCompris]"), NULL},
217

218
  {"experimental",'\0', 0, G_OPTION_ARG_NONE, &popt_experimental,
219
   N_("Run the experimental activities"), NULL},
220

221
  {"disable-quit",'\0', 0, G_OPTION_ARG_NONE, &popt_no_quit,
Bruno Coudoin's avatar
Bruno Coudoin committed
222
   N_("Disable the quit button"), NULL},
223

224
  {"disable-config",'\0', 0, G_OPTION_ARG_NONE, &popt_no_config,
Bruno Coudoin's avatar
Bruno Coudoin committed
225
   N_("Disable the config button"), NULL},
226

227 228 229 230 231 232 233 234 235 236
  {"server", '\0', 0, G_OPTION_ARG_STRING, &popt_server,
   N_("GCompris will get images, sounds and activity data from this server if not found locally."), NULL},

  {"web-only", '\0', 0, G_OPTION_ARG_NONE, &popt_web_only,
   N_("Only when --server is provided, disable check for local resource first."
      " Data are always taken from the web server."), NULL},

  {"cache-dir", '\0', 0, G_OPTION_ARG_STRING, &popt_cache_dir,
   N_("In server mode, specify the cache directory used to avoid useless downloads."), NULL},

237
  {"drag-mode", 'g', 0, G_OPTION_ARG_STRING, &popt_drag_mode,
238
   N_("Global drag and drop mode: normal, 2clicks, both. Default mode is normal."), NULL},
239

240 241 242
  {"nolockcheck", '\0', 0, G_OPTION_ARG_NONE, &popt_nolockcheck,
   N_("Do not avoid the execution of multiple instances of GCompris."), NULL},

243 244 245 246 247 248
  {"sugarBundleId", '\0', 0, G_OPTION_ARG_STRING, &sugarBundleId,
   "Sugar Bundle Id", NULL},

  {"sugarActivityId", '\0', 0, G_OPTION_ARG_STRING, &sugarActivityId,
   "Sugar Activity Id", NULL},

249
  { NULL }
250 251
};

252
/* Remove any dialog box */
253
static void gc_close_all_dialog() {
254 255
  gc_dialog_close();
  gc_help_stop();
256
  gc_config_stop();
257 258 259
  gc_about_stop();
  gc_selector_file_stop();
  gc_selector_images_stop();
260 261
}

262 263 264 265 266 267 268 269
/* Return the zoom factor we are currently using for our window
 *
 */
double gc_zoom_factor_get()
{
  return zoom_factor;
}

270 271 272 273 274 275 276 277 278 279 280 281
static gint
_gc_configure_event_callback (GtkWidget   *widget,
			      GdkEventConfigure *event,
			      gpointer     client_data)
{
  double xratio, yratio;
  gint screen_height, screen_width;

  gdk_drawable_get_size(GDK_DRAWABLE (window->window),
			&screen_width,
			&screen_height);

282
  yratio=screen_height/(float)(BOARDHEIGHT);
283
  xratio=screen_width/(float)BOARDWIDTH;
284
  zoom_factor = MIN(xratio, yratio);
285 286 287
  g_message("The screen_width=%f screen_height=%f ratio=%f\n",
	    (double)screen_width, (double)screen_height, zoom_factor);

288 289 290
  gtk_widget_set_usize (canvas, BOARDWIDTH*zoom_factor, BOARDHEIGHT*zoom_factor);
  goo_canvas_set_scale (GOO_CANVAS(canvas), zoom_factor);
  gtk_fixed_move(GTK_FIXED(fixed), canvas,
291
		 (screen_width-BOARDWIDTH*zoom_factor)/2,
292
		 (screen_height-BOARDHEIGHT*zoom_factor)/2);
293

294 295
  _expose_background_callback (drawing_area, NULL, NULL);

296 297 298
  return FALSE;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
299 300
static gint
board_widget_key_press_callback (GtkWidget   *widget,
301 302
				 GdkEventKey *event,
				 gpointer     client_data)
Bruno Coudoin's avatar
Bruno Coudoin committed
303
{
304
  int kv = event->keyval;
305

306 307
  if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_r)
					 || (event->keyval == GDK_R))) {
308
    g_message("Refreshing the canvas\n");
309
    goo_canvas_update(GOO_CANVAS(canvas));
310 311
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
312
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_q)
313
					 || (event->keyval == GDK_Q))) {
314
    gc_exit();
315 316
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
317 318 319 320 321 322 323 324 325
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_f)
					 || (event->keyval == GDK_F))) {
    /* Toggle fullscreen */
    if (fullscreen)
      gc_fullscreen_set(FALSE);
    else
      gc_fullscreen_set(TRUE);
    return TRUE;
  }
326

Bruno Coudoin's avatar
Bruno Coudoin committed
327 328 329
  switch (event->keyval)
    {
    case GDK_Escape:
330
      gc_close_all_dialog();
331

332
      if (gc_board_get_current()->previous_board != NULL)
333
	gc_board_stop();
Bruno Coudoin's avatar
Bruno Coudoin committed
334
      return TRUE;
335
    case GDK_F5:
336
      g_message("Refreshing the canvas\n");
337
      goo_canvas_update(GOO_CANVAS(canvas));
338
      return TRUE;
339

340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
    case GDK_KP_Multiply:
      break;
    case GDK_KP_0:
    case GDK_KP_Insert:
      event->keyval=GDK_0;
      break;
    case GDK_KP_1:
    case GDK_KP_End:
      event->keyval=GDK_1;
      break;
    case GDK_KP_2:
    case GDK_KP_Down:
      event->keyval=GDK_2;
      break;
    case GDK_KP_3:
    case GDK_KP_Page_Down:
      event->keyval=GDK_3;
      break;
    case GDK_KP_4:
    case GDK_KP_Left:
      event->keyval=GDK_4;
      break;
    case GDK_KP_5:
    case GDK_KP_Begin:
      event->keyval=GDK_5;
      break;
    case GDK_KP_6:
    case GDK_KP_Right:
      event->keyval=GDK_6;
      break;
    case GDK_KP_7:
    case GDK_KP_Home:
      event->keyval=GDK_7;
      break;
    case GDK_KP_8:
    case GDK_KP_Up:
      event->keyval=GDK_8;
      break;
    case GDK_KP_9:
    case GDK_KP_Page_Up:
      event->keyval=GDK_9;
      break;
382
    default:
383 384 385 386
      break;
    }

  /* pass through the IM context */
387
  if (gc_board_get_current() && (!gc_board_get_current()->disable_im_context))
388 389
    {
      if (gtk_im_context_filter_keypress (properties->context, event))
Bruno Coudoin's avatar
Bruno Coudoin committed
390
	{
391
	  g_message("%d key is handled by context", kv);
392
	  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
393 394 395
	}
    }

396
  g_message("%d key is NOT handled by context", kv);
397 398 399 400
  /* If the board needs to receive key pressed */
  /* NOTE: If a board receives key press, it must bind the ENTER Keys to OK
   *       whenever possible
   */
401
  if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
402
      && gc_board_get_current_board_plugin()->key_press)
403
    {
404
      return(gc_board_get_current_board_plugin()->key_press (event->keyval, NULL, NULL));
405
    }
406
  else if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
407
	   && gc_board_get_current_board_plugin()->ok &&
408 409 410
	   (event->keyval == GDK_KP_Enter ||
	    event->keyval == GDK_Return   ||
	    event->keyval == GDK_KP_Space))
411 412
    {
      /* Else we send the OK signal. */
413
      gc_board_get_current_board_plugin()->ok ();
414 415 416
      return TRUE;
    }

417
  /* Event not handled; try parent item */
Bruno Coudoin's avatar
Bruno Coudoin committed
418 419 420
  return FALSE;
};

421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
void gc_cursor_set(guint gdk_cursor_type)
{
  GdkCursor *cursor = NULL;

  // Little hack to force gcompris to use the default cursor
  if(gdk_cursor_type==GCOMPRIS_DEFAULT_CURSOR)
    gdk_cursor_type=properties->defaultcursor;

  // I suppose there is less than GCOMPRIS_FIRST_CUSTOM_CURSOR cursors defined in gdkcursors.h !
  if (gdk_cursor_type < GCOMPRIS_FIRST_CUSTOM_CURSOR) {
    cursor = gdk_cursor_new(gdk_cursor_type);
    gdk_window_set_cursor (window->window, cursor);
    gdk_cursor_destroy(cursor);
  } else { // we use a custom cursor
    GdkPixbuf *cursor_pixbuf = NULL;

    switch (gdk_cursor_type) {
    case GCOMPRIS_DEFAULT_CURSOR :
439
      cursor_pixbuf = gc_skin_pixmap_load("cursors/default.png");
440 441
      break;
    case GCOMPRIS_LINE_CURSOR :
442
      cursor_pixbuf = gc_skin_pixmap_load("cursors/line.png");
443 444
      break;
    case GCOMPRIS_RECT_CURSOR :
445
      cursor_pixbuf = gc_skin_pixmap_load("cursors/rect.png");
446 447
      break;
    case GCOMPRIS_FILLRECT_CURSOR :
448
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fillrect.png");
449 450
      break;
    case GCOMPRIS_CIRCLE_CURSOR :
451
      cursor_pixbuf = gc_skin_pixmap_load("cursors/circle.png");
452 453
      break;
    case GCOMPRIS_FILLCIRCLE_CURSOR :
454
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fillcircle.png");
455 456
      break;
    case GCOMPRIS_FILL_CURSOR :
457
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fill.png");
458 459
      break;
    case GCOMPRIS_DEL_CURSOR :
460
      cursor_pixbuf = gc_skin_pixmap_load("cursors/del.png");
461 462
      break;
    case GCOMPRIS_SELECT_CURSOR :
463
      cursor_pixbuf = gc_skin_pixmap_load("cursors/select.png");
464 465 466 467 468 469 470 471
      break;
    default :
      return;
      break;
    }

    if(cursor_pixbuf)
      {
472 473
	cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
					    cursor_pixbuf, 0, 0);
474 475 476 477 478 479 480
	gdk_window_set_cursor(window->window, cursor);
	gdk_cursor_unref(cursor);
	gdk_pixbuf_unref(cursor_pixbuf);
      }
  }
}

Bruno Coudoin's avatar
Bruno Coudoin committed
481 482 483
/**
 * Return the main canvas we run in
 */
484
GooCanvas *gc_get_canvas()
Bruno Coudoin's avatar
Bruno Coudoin committed
485
{
486
  return GOO_CANVAS (canvas);
Bruno Coudoin's avatar
Bruno Coudoin committed
487 488
}

489
GtkWidget *gc_get_window()
490 491 492 493
{
  return window;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
494
static void
495
_set_svg_background(GooCanvasItem *parent, gchar *file, gchar *id)
Bruno Coudoin's avatar
Bruno Coudoin committed
496
{
Bruno Coudoin's avatar
Bruno Coudoin committed
497
  RsvgHandle *rsvg_handle;
Bruno Coudoin's avatar
Bruno Coudoin committed
498

499
  rsvg_handle = gc_rsvg_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
500 501 502

  if(backgroundsvgimg)
    g_object_set(backgroundsvgimg,
503
		 "svg-handle", rsvg_handle,
504
		 "svg-id", id,
Bruno Coudoin's avatar
Bruno Coudoin committed
505 506
		 NULL);
  else
507
    backgroundsvgimg = goo_canvas_svg_new (parent,
Bruno Coudoin's avatar
Bruno Coudoin committed
508 509 510 511 512 513 514 515 516 517 518 519
					 rsvg_handle,
					 NULL);
  goo_canvas_item_lower(backgroundsvgimg, NULL);

  g_object_unref(rsvg_handle);
}

static void
_set_pixmap_background(GooCanvasItem *parent, gchar *file)
{
  GdkPixbuf *background_pixmap;

520
  background_pixmap = gc_pixmap_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
521 522

  if(backgroundimg)
523 524 525
    g_object_set(backgroundimg,
		 "pixbuf", background_pixmap,
		 NULL);
526
  else
527 528 529 530 531 532 533 534
    backgroundimg = goo_canvas_image_new (parent,
					  background_pixmap,
					  0.0,
					  0.0,
					  "width", (gdouble) BOARDWIDTH,
					  "height", (gdouble) BOARDHEIGHT,
					  NULL);
  goo_canvas_item_lower(backgroundimg, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
535 536 537

  gdk_pixbuf_unref(background_pixmap);

Bruno Coudoin's avatar
Bruno Coudoin committed
538 539
}

540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555
void _clear_svg_background()
{
  if(backgroundsvgimg)
    goo_canvas_item_remove(backgroundsvgimg);

  backgroundsvgimg = NULL;
}

void _clear_pixmap_background()
{
  if(backgroundimg)
    goo_canvas_item_remove(backgroundimg);

  backgroundimg = NULL;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
556 557 558 559 560 561 562 563 564 565 566
void
gc_set_background(GooCanvasItem *parent, gchar *file)
{
  g_assert(parent);
  g_assert(file);

  if(g_str_has_suffix(file, ".svg") ||
     g_str_has_suffix(file, ".svgz"))
    {
      if(backgroundimg)
	goo_canvas_item_remove(backgroundimg);
567 568

      backgroundimg = NULL;
569
      _set_svg_background(parent, file, NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
570 571 572
    }
  else
    {
573
      _clear_svg_background();
Bruno Coudoin's avatar
Bruno Coudoin committed
574 575
      _set_pixmap_background(parent, file);
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
576 577
}

578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600
void
gc_set_background_by_id(GooCanvasItem *parent, RsvgHandle *rsvg_handle,
			gchar *id)
{
  g_assert(parent);
  g_assert(rsvg_handle);

  _clear_pixmap_background();

  if(backgroundsvgimg)
    g_object_set(backgroundsvgimg,
		 "svg-handle", rsvg_handle,
		 "svg-id", id,
		 NULL);
  else
    backgroundsvgimg = goo_canvas_svg_new (parent,
					   rsvg_handle,
					   "svg-id", id,
					   NULL);

  goo_canvas_item_lower(backgroundsvgimg, NULL);
}

601 602 603 604 605 606 607 608
void
gc_set_default_background(GooCanvasItem *parent)
{
  gc_set_background_by_id(parent,
			  gc_skin_rsvg_get(),
			  "#BACKGROUND");
}

609 610 611
/* Redraw the black background
 */
static gboolean
612 613
_expose_background_callback (GtkWidget *widget,
			     GdkEventExpose *event, gpointer data)
614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629
{
  gint screen_height, screen_width;

  gdk_drawable_get_size(GDK_DRAWABLE (window->window),
			&screen_width,
			&screen_height);

  gtk_widget_set_size_request (widget, screen_width, screen_height);
  gdk_draw_rectangle (widget->window,
		      widget->style->fg_gc[GTK_WIDGET_STATE (widget)],
		      TRUE,
		      0, 0, widget->allocation.width, widget->allocation.height);

  return FALSE;
}

630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656
/*
 * Sugar requires properties to be set before the windows is realized
 */
static gboolean
_realize_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
  Window xwindow = GDK_WINDOW_XWINDOW(window->window);

  if (sugarBundleId)
    XChangeProperty(GDK_DISPLAY(),
		    xwindow,
		    XInternAtom(GDK_DISPLAY(), "_SUGAR_BUNDLE_ID", 0),
		    XInternAtom(GDK_DISPLAY(), "STRING", 0), 8,
		    PropModeReplace,
		    (unsigned char *) sugarBundleId, strlen(sugarBundleId));

  if (sugarActivityId)
    XChangeProperty(GDK_DISPLAY(),
		    xwindow,
		    XInternAtom(GDK_DISPLAY(), "_SUGAR_ACTIVITY_ID", 0),
		    XInternAtom(GDK_DISPLAY(), "STRING", 0), 8,
		    PropModeReplace,
		    (unsigned char *) sugarActivityId, strlen(sugarActivityId));

  return FALSE;
}

657 658
static void
init_background()
Bruno Coudoin's avatar
Bruno Coudoin committed
659
{
660
  drawing_area = gtk_drawing_area_new ();
661
  gtk_widget_set_size_request (drawing_area, BOARDWIDTH, BOARDHEIGHT);
662 663
  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
		    G_CALLBACK (_expose_background_callback), NULL);
664
  /* Create a vertical box in which I put first the play board area, then the button bar */
665 666
  fixed = gtk_fixed_new ();
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(fixed));
667

668
  gtk_fixed_put (GTK_FIXED(fixed), GTK_WIDGET(drawing_area), 0, 0);
669 670 671
  gtk_fixed_put (GTK_FIXED(fixed), GTK_WIDGET(canvas), 0, 0);

  gtk_widget_show (GTK_WIDGET(fixed));
672 673
  gtk_widget_show (GTK_WIDGET(canvas));

674
  gtk_widget_set_usize (GTK_WIDGET(canvas), BOARDWIDTH, BOARDHEIGHT);
675 676 677 678 679
  goo_canvas_set_bounds (GOO_CANVAS(canvas),
			 0, 0,
			 BOARDWIDTH,
			 BOARDHEIGHT);

680
}
Bruno Coudoin's avatar
Bruno Coudoin committed
681

682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728
static GcomprisBoard *get_board_to_start()
{
  GcomprisBoard *board_to_start;

  /* By default, the menu will be started */
  board_to_start = properties->menu_board;

  /* Get and Run the root menu */
  if(properties->administration)
    {
      board_to_start = gc_menu_section_get("/administration/administration");
    }
  else
    {
      /* If we have a profile defined, run the login screen
       * (the login screen is a board that uppon login completion
       * starts the menu)
       */
      if(properties->profile && properties->profile->group_ids)
	{
	  gboolean found = FALSE;

	  GList *group_id;

	  for (group_id = properties->profile->group_ids; group_id != NULL; group_id = group_id->next)
	    if (g_list_length(gc_db_users_from_group_get( *((int *) group_id->data))) > 0){
	      found = TRUE;
	      break;
	    }

	  /* No profile start normally */
	  if (found)
	    board_to_start = gc_menu_section_get("/login/login");
	  else {
	    board_to_start = gc_menu_section_get(properties->root_menu);
	    /* this will set user information to system one */
	    gc_profile_set_current_user(NULL);
	  }
	}
      else
	/* this will set user information to system one */
	gc_profile_set_current_user(NULL);
    }

  return board_to_start;
}

729 730
static void setup_window ()
{
731
  GcomprisBoard *board_to_start;
732 733

  window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
Bruno Coudoin's avatar
Bruno Coudoin committed
734

735 736 737 738
  /*
   * Set an icon for gcompris
   * ------------------------
   */
739 740 741 742 743 744 745
  {
    GdkPixbuf *icon_pixbuf = NULL;
    gchar *iconfile = gc_file_find_absolute("%s/%s",
					    properties->system_icon_dir, "gcompris.png",
					    NULL);
    if(iconfile)
      {
746
	icon_pixbuf = gdk_pixbuf_new_from_file(iconfile, NULL);
747 748 749 750 751 752 753 754 755 756
	g_free(iconfile);

	if (icon_pixbuf)
	  {
	    gtk_window_set_icon (GTK_WINDOW (window), icon_pixbuf);
	    gdk_pixbuf_unref (icon_pixbuf);
	  }
      }
    else
      g_message ("Failed to find icon file: 'gcompris.png'");
757

758
  }
759

760
  gtk_window_set_title(GTK_WINDOW (window), "GCompris");
761

762 763 764 765 766 767 768
  GdkGeometry hints;
  hints.base_width = 174;
  hints.base_height = 144;
  hints.min_width = 174;
  hints.min_height = 144;
  hints.width_inc = 1;
  hints.height_inc = 1;
769
  hints.min_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
770
  hints.max_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
771 772 773 774 775
  gtk_window_set_geometry_hints (GTK_WINDOW (window),
				 NULL,
				 &hints,
				 GDK_HINT_RESIZE_INC |
				 GDK_HINT_MIN_SIZE |
776 777
				 GDK_HINT_BASE_SIZE |
				 GDK_HINT_ASPECT);
778

779 780 781 782
  /*
   * Set the main window
   * -------------------
   */
783

784
  gtk_window_set_default_size(GTK_WINDOW(window), BOARDWIDTH, BOARDHEIGHT);
785
  gtk_window_set_wmclass(GTK_WINDOW(window), "gcompris", "GCompris");
Bruno Coudoin's avatar
Bruno Coudoin committed
786

787 788 789
  g_signal_connect (GTK_OBJECT (window), "realize",
		    G_CALLBACK (_realize_callback), NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
790
  gtk_widget_realize (window);
Bruno Coudoin's avatar
Bruno Coudoin committed
791

Bruno Coudoin's avatar
Bruno Coudoin committed
792 793 794
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
		      GTK_SIGNAL_FUNC (quit_cb), NULL);

795 796
  gtk_signal_connect (GTK_OBJECT (window), "map_event",
		      GTK_SIGNAL_FUNC (map_cb), NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
797

798
  gtk_signal_connect (GTK_OBJECT (window), "configure_event",
799
		      GTK_SIGNAL_FUNC (_gc_configure_event_callback), NULL);
800

801
  // Set the cursor
802
  gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
803

804
  canvas     = goo_canvas_new();
Bruno Coudoin's avatar
Bruno Coudoin committed
805

806 807 808 809
  g_object_set (G_OBJECT(goo_canvas_get_root_item(GOO_CANVAS(canvas))),
		"can-focus", TRUE,
		NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
810

811 812
  gtk_signal_connect_after (GTK_OBJECT (window), "key_press_event",
			    GTK_SIGNAL_FUNC (board_widget_key_press_callback), 0);
Bruno Coudoin's avatar
Bruno Coudoin committed
813 814 815 816 817 818
  g_signal_connect_after (canvas,
			  "key_press_event",
			  GTK_SIGNAL_FUNC (board_widget_key_press_callback), 0);

  GTK_WIDGET_SET_FLAGS (canvas, GTK_CAN_FOCUS);
  gtk_widget_grab_focus (canvas);
819

820
  gc_im_init(window);
821

822
  gc_board_init();
823

824
  /* Load all the menu once */
825
  gc_menu_load();
826

827
  /* Save the root_menu */
828
  properties->menu_board = gc_menu_section_get(properties->root_menu);
829

830
  /* Run the bar */
831
  gc_bar_start(GOO_CANVAS(canvas));
832 833 834

  init_background();

835 836
  board_to_start = get_board_to_start();

837
  if(!board_to_start) {
838 839 840
    gchar *tmpstr= g_strdup_printf("Couldn't find the board menu %s, or plugin execution error", properties->root_menu);
    gc_dialog(tmpstr, NULL);
    g_free(tmpstr);
841
  } else if(!gc_board_check_file(board_to_start)) {
842 843 844
    gchar *tmpstr= g_strdup_printf("Couldn't find the board menu, or plugin execution error");
    gc_dialog(tmpstr, NULL);
    g_free(tmpstr);
845
  } else {
846
    g_message("Fine, we got the gcomprisBoardMenu, xml boards parsing went fine");
847 848
    if(!display_activation_dialog())
      gc_board_play(board_to_start);
849
  }
850

851 852 853

}

854
#ifdef STATIC_MODULE
855 856
/** Display the activation dialog for the windows version
 *
857
 * return TRUE is the dialog is display, FALSE instead.
858
 */
859
int
860 861
display_activation_dialog()
{
862
  int board_count = 0;
863
  int gc_board_number_in_demo = 0;
864
  GList *list;
865 866
  guint  key_is_valid = 0;

Bruno Coudoin's avatar
Bruno Coudoin committed
867
  key_is_valid = gc_activation_check(properties->key);
868

Bruno Coudoin's avatar
Bruno Coudoin committed
869
  if(key_is_valid == 1)
870
    return FALSE;
871 872 873 874 875 876

  /* Count non menu boards */
  for (list = gc_menu_get_boards(); list != NULL; list = list->next)
    {
      GcomprisBoard *board = list->data;
      if (strcmp(board->type, "menu") != 0 &&
877 878 879 880 881 882 883
	  strncmp(board->section, "/experimental", 13) != 0)
	{
	  board_count++;
	  gc_board_check_file(board);
	  if(board->plugin)
	      gc_board_number_in_demo++;
	}
884 885
    }

886 887
  /* Entry area */
  widget_activation_entry = (GtkEntry *)gtk_entry_new();
Bruno Coudoin's avatar
Bruno Coudoin committed
888
  gtk_entry_set_max_length(widget_activation_entry, 6);
889
  activation_item = \
890
    goo_canvas_item_new (goo_canvas_get_root_item(canvas),
891
			   goo_canvas_widget_get_type (),
892 893 894 895 896 897 898 899 900 901 902 903 904 905 906
			   "widget", GTK_WIDGET(widget_activation_entry),
			   "x", (double) BOARDWIDTH / 2 - 50,
			   "y", (double) BOARDHEIGHT - 60,
			   "width", 100.0,
			   "height", 30.0,
			   "anchor", GTK_ANCHOR_NW,
			   "size_pixels", FALSE,
			   NULL);
  gtk_signal_connect(GTK_OBJECT(widget_activation_entry), "activate",
		     GTK_SIGNAL_FUNC(activation_enter_callback),
		     NULL);

  gtk_widget_show(GTK_WIDGET(widget_activation_entry));
  gtk_entry_set_text(GTK_ENTRY(widget_activation_entry), "CODE");

907
  char *msg = g_strdup_printf(_("GCompris is free software released under the GPL License. In order to support its development, the Windows version provides only %d of the %d activities. You can get the full version for a small fee at\n<http://gcompris.net>\nThe GNU/Linux version does not have this restriction. Note that GCompris is being developed to free schools from monopolistic software vendors. If you also believe that we should teach freedom to children, please consider using GNU/Linux. Get more information at FSF:\n<http://www.fsf.org/philosophy>"),
908 909 910
			      gc_board_number_in_demo, board_count);
  gc_dialog(msg, activation_done);
  g_free(msg);
911 912

  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
913 914
}

915 916 917 918 919 920 921
/**
 * Return -1 if the code is not valid
 *        0  if the code is valid but out of date
 *        1  if the code is valid and under 2 years
 */
int gc_activation_check(char *code)
{
Bruno Coudoin's avatar
Bruno Coudoin committed
922
#ifdef  DISABLE_ACTIVATION_CODE
Bruno Coudoin's avatar
Bruno Coudoin committed
923
  return 1;
Bruno Coudoin's avatar
Bruno Coudoin committed
924
#else
925 926 927 928 929 930 931 932 933 934 935 936 937
  int value = 0;
  int i;
  char crc1 = 0;
  char crc2 = 0;
  char codeddate[4];

  if(strlen(code) != 6)
    return -1;

  for(i=3; i>=0; i--)
    {
      value |= code[i] & 0x07;
      value = value << 3;
Bruno Coudoin's avatar
Bruno Coudoin committed
938
      crc1 = (crc1 ^ code[i]) & 0x07;
939 940 941 942
    }
  value = value >> 3;
  crc1 = 0x30 | crc1;
  crc2 = 0x30 | (code[2] ^ code[3]);
943

Bruno Coudoin's avatar
Bruno Coudoin committed
944 945 946 947 948
  if(crc1 != code[4])
    return(-1);

  if(crc2 != code[5])
    return(-1);
949

Bruno Coudoin's avatar
Bruno Coudoin committed
950
  codeddate[3] = 0x30 | (value & 0x000F);
951
  value = value >> 4;
952

Bruno Coudoin's avatar
Bruno Coudoin committed
953
  codeddate[2] = 0x30 | (value & 0x0001);
954
  value = value >> 1;
955

Bruno Coudoin's avatar
Bruno Coudoin committed
956
  codeddate[1] = 0x30 | (value & 0x000F);
957
  value = value >> 4;
958

Bruno Coudoin's avatar
Bruno Coudoin committed
959
  codeddate[0] = 0x30 | (value & 0x0003);
960 961 962 963 964 965
  codeddate[4] = '\0';

  if(atoi(codeddate) + 200 >= atoi(BUILD_DATE))
    return(1);
  else
    return(0);
Bruno Coudoin's avatar
Bruno Coudoin committed
966
#endif
967 968
}

969 970 971 972
/* Check the activation code
 *
 */
static void
973 974
activation_enter_callback( GtkWidget *entry,
			   GtkWidget *notused )
975
{
976
  switch(gc_activation_check((char *)gtk_entry_get_text(GTK_ENTRY(entry))))
977
    {
978
    case 1:
Bruno Coudoin's avatar
Bruno Coudoin committed
979 980
      gc_prop_get()->key = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
      gc_prop_save(properties);
981 982 983 984 985 986 987 988
      gtk_entry_set_text(GTK_ENTRY(entry), "GOOD");
      break;
    case 0:
      gtk_entry_set_text(GTK_ENTRY(entry), "EXPIRE");
      break;
    case -1:
      gtk_entry_set_text(GTK_ENTRY(entry), "WRONG");
      break;
989 990 991 992 993 994 995 996 997
    }
}

/* Callback for the activation dialog
 *
 */
static void
activation_done()
{
998 999 1000 1001
  if ((strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "CODE") != 0) &&
      (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "GOOD") != 0) &&
      (strcmp((char *)gtk_entry_get_text(GTK_ENTRY(widget_activation_entry)), "WRONG") != 0))
    {
1002
      activation_enter_callback(GTK_WIDGET(widget_activation_entry), NULL);
1003
    }
1004

1005
  gc_board_play( get_board_to_start());
1006 1007 1008 1009 1010 1011 1012
  gtk_object_destroy (GTK_OBJECT(activation_item));
}
#endif

/** Call me to end an activity
 *
 */
1013
void gc_board_end()
Bruno Coudoin's avatar
Bruno Coudoin committed
1014
{
1015
  if (gc_board_get_current()->previous_board) {
1016
    /* Run the previous board */
1017
    gc_board_play(gc_board_get_current()->previous_board);
1018
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
1019 1020
}

1021 1022 1023 1024
/** \brief toggle full screen mode
 *
 *
 */
1025
void gc_fullscreen_set(gboolean state)
1026
{
Bruno Coudoin's avatar
Bruno Coudoin committed
1027
  fullscreen = state;
1028 1029 1030
  if(state)
    {
      gdk_window_set_decorations (window->window, 0);
1031
      gtk_window_fullscreen (GTK_WINDOW(window));
1032
      gtk_widget_set_uposition (window, 0, 0);
1033 1034
    }
  else
1035
    {
1036
      gdk_window_set_decorations (window->window, GDK_DECOR_ALL);
1037
      gtk_window_unfullscreen (GTK_WINDOW(window));
Bruno Coudoin's avatar
Bruno Coudoin committed
1038 1039 1040

      /* Mandatory or on windows we get iconified */
      gtk_window_deiconify (GTK_WINDOW(window));
1041 1042 1043 1044
    }

}

1045
/* Use these instead of the goo_canvas ones for proper fullscreen mousegrab
1046
   handling. */
1047 1048
int gc_canvas_item_grab (GooCanvasItem *item, unsigned int event_mask,
			 GdkCursor *cursor, guint32 etime)
1049 1050
{
  int retval;
1051

1052 1053
  retval = goo_canvas_pointer_grab(goo_canvas_item_get_canvas(item), item,
				   event_mask, cursor, etime);
1054 1055
  if (retval != GDK_GRAB_SUCCESS)
    return retval;
1056

1057 1058 1059
  return retval;
}

1060
void gc_canvas_item_ungrab (GooCanvasItem *item, guint32 etime)
1061
{
1062
  goo_canvas_pointer_ungrab(goo_canvas_item_get_canvas(item), item, etime);
1063 1064
}

1065
static void cleanup()
Bruno Coudoin's avatar
Bruno Coudoin committed
1066
{
1067 1068 1069 1070
  /* Do not loopback in exit */
  signal(SIGINT,  NULL);
  signal(SIGSEGV, NULL);

1071
  single_instance_release(); /* Must be done before property destroy */
1072
  gc_board_stop();
1073
  gc_db_exit();
1074
  gc_fullscreen_set(FALSE);
1075
  gc_menu_destroy();
1076 1077
  gc_net_destroy();
  gc_cache_destroy();
1078
  gc_prop_destroy(gc_prop_get());
1079
}
1080

1081
void gc_exit()
1082 1083
{
  g_signal_emit_by_name(G_OBJECT(window), "delete_event");
1084
}
Bruno Coudoin's avatar
Bruno Coudoin committed
1085

1086 1087
static void quit_cb (GtkWidget *widget, gpointer data)
{
1088 1089 1090 1091

#ifdef DMALLOC
  dmalloc_shutdown();
#endif
1092 1093
  cleanup();
  gtk_main_quit();
1094

1095 1096 1097 1098 1099
  /*
   * Very important or GCompris crashes on exit when closed from the dialog
   * It's like if code in the dialog callback continue after the gtk_main_quit is done
   */
  exit(0);
Bruno Coudoin's avatar
Bruno Coudoin committed
1100 1101
}

1102 1103 1104 1105 1106 1107 1108 1109
/*
 * We want GCompris to be set as fullscreen the later possible
 *
 */
static void map_cb (GtkWidget *widget, gpointer data)
{
  if(is_mapped == FALSE)
    {
1110
      gc_fullscreen_set(properties->fullscreen);
Bruno Coudoin's avatar
Bruno Coudoin committed
1111
      is_mapped = TRUE;
1112
    }
1113
  g_message("gcompris window is now mapped");
1114 1115
}

1116
/*
1117
 * Process the cleanup of the child (no zombies)
1118 1119
 * ---------------------------------------------
 */
1120
void gc_terminate(int signum)
1121 1122
{

1123
  g_message("GCompris got the %d signal, starting exit procedure", signum);
1124

1125
  gc_exit();
1126

1127 1128
}

Bruno Coudoin's avatar
Bruno Coudoin committed
1129 1130
static void load_properties ()
{
1131
  properties = gc_prop_new ();
1132 1133 1134 1135 1136

  /* Initialize the binary relocation API
   *  http://autopackage.org/docs/binreloc/
   */
  if(gbr_init (NULL))
1137
    g_message("Binary relocation enabled");
1138
  else
1139
    g_message("Binary relocation disabled");
1140

1141 1142
  /* usefull for OSX bundle app */
  /* FIXME exec_prefix should be put in properties */
Bruno Coudoin's avatar
Bruno Coudoin committed
1143
  /* usefull for OSX bundle app */
1144 1145 1146
#ifdef NSBUNDLE
  exec_prefix = gcompris_nsbundle_resource ();
#else
Bruno Coudoin's avatar
Bruno Coudoin committed
1147
  exec_prefix = gbr_find_exe_dir(NULL);
1148
#endif
Bruno Coudoin's avatar
Bruno Coudoin committed
1149
  g_warning("exec_prefix %s\n", (exec_prefix==NULL ? "NONE" : exec_prefix));
1150

1151 1152 1153 1154 1155
  {
    gchar *pkg_data_dir = gbr_find_data_dir(PACKAGE_DATA_DIR);
    gchar *pkg_clib_dir = gbr_find_lib_dir(PACKAGE_CLIB_DIR);

    properties->package_data_dir = g_strconcat(pkg_data_dir, "/gcompris/boards", NULL);
1156
    properties->package_skin_dir = g_strconcat(pkg_data_dir, "/gcompris/boards/skins", NULL);
1157 1158 1159 1160 1161 1162 1163 1164 1165
    properties->package_locale_dir = gbr_find_locale_dir(PACKAGE_LOCALE_DIR);
    properties->package_plugin_dir = g_strconcat(pkg_clib_dir, "/gcompris", NULL);
    properties->package_python_plugin_dir = g_strconcat(pkg_data_dir, "/gcompris/python",
							NULL);
    properties->system_icon_dir = g_strconcat(pkg_data_dir, "/pixmaps", NULL);
    properties->menu_dir = g_strdup(properties->package_data_dir);
    g_free(pkg_data_dir);
    g_free(pkg_clib_dir);
  }
1166

1167
  /* Display the directory value we have */
1168
  printf("package_data_dir         = %s\n", properties->package_data_dir);
1169
  printf("package_skin_dir         = %s\n", properties->package_skin_dir);
1170
  printf("package_menu_dir         = %s\n", properties->menu_dir);
1171 1172 1173
  printf("package_locale_dir       = %s\n", properties->package_locale_dir);
  printf("package_plugin_dir       = %s\n", properties->package_plugin_dir);
  printf("package_python_plugin_dir= %s\n", properties->package_python_plugin_dir);
Bruno Coudoin's avatar
Bruno Coudoin committed
1174 1175
}

1176
GcomprisProperties *gc_prop_get ()
1177 1178 1179 1180
{
  return (properties);
}

1181 1182 1183
/* Return the database file name
 * Must be called after properties is initialised
 */
1184
gchar *gc_db_get_filename ()
1185 1186 1187 1188 1189 1190
{
  g_assert(properties!=NULL);

  return (properties->database);
}

Bruno Coudoin's avatar
Bruno Coudoin committed
1191 1192 1193 1194
/*
 * This returns the locale for which text must be displayed
 *
 */
1195
const gchar *gc_locale_get()
Bruno Coudoin's avatar
Bruno Coudoin committed
1196
{
1197
  const gchar *locale;
1198

1199
  /* First check locale got overrided by the user */
1200 1201
  if(gc_locale != NULL)
    return(gc_locale);
1202

1203
  locale = g_getenv("LC_ALL");
1204
  if(locale == NULL)
1205
    locale = g_getenv("LC_CTYPE");
1206
  if(locale == NULL)
1207
    locale = g_getenv("LANG");
1208 1209 1210 1211

  if(locale!=NULL)
    return(locale);

1212
  return("en_US.UTF-8");
Bruno Coudoin's avatar
Bruno Coudoin committed
1213 1214
}

1215 1216 1217 1218 1219
/*
 * This return the user default locale like it was at program
 * startup before we started changing the locale
 *
 */
1220
char *gc_locale_get_user_default()
1221
{
1222
  return gc_user_default_locale;
1223 1224
}