gcompris.c 46.5 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 139
static gint  popt_experimental     = FALSE;
static gint  popt_no_quit	   = FALSE;
static gint  popt_no_config        = FALSE;
static gchar *popt_drag_mode       = NULL;
140 141
static gchar *sugarBundleId        = NULL;
static gchar *sugarActivityId      = NULL;
142 143 144

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

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

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

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

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

159
  {"difficulty", 'd', 0, G_OPTION_ARG_INT, &popt_difficulty_filter,
160
   N_("display only activities with this difficulty level."), NULL},
161

162
  {"debug", 'D', 0, G_OPTION_ARG_NONE, &popt_debug,
163
   N_("display debug informations on the console."), NULL},
164

165
  {"version", 'v', 0, G_OPTION_ARG_NONE, &popt_version,
166
   N_("Print the version of " PACKAGE), NULL},
167

168
  {"root-menu", 'l', 0, G_OPTION_ARG_STRING, &popt_root_menu,
Bruno Coudoin's avatar
Bruno Coudoin committed
169 170 171
   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},
172

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

176 177 178
  {"package_skin_dir", 'S', 0, G_OPTION_ARG_STRING, &popt_package_skin_dir,
   N_("GCompris will find the skins in this directory"), NULL},

179 180 181 182 183
  {"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},
184

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

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

191
  {"administration", 'a', 0, G_OPTION_ARG_NONE, &popt_administration,
192
   N_("Run GCompris in administration and user-management mode"), NULL},
193

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

197
  {"create-db",'\0', 0, G_OPTION_ARG_NONE, &popt_create_db,
198
   N_("Create the alternate database for profiles"), NULL},
199

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

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

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

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

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

215
  {"experimental",'\0', 0, G_OPTION_ARG_NONE, &popt_experimental,
216
   N_("Run the experimental activities"), NULL},
217

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

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

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

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

230 231 232 233 234 235
  {"sugarBundleId", '\0', 0, G_OPTION_ARG_STRING, &sugarBundleId,
   "Sugar Bundle Id", NULL},

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

236
  { NULL }
237 238
};

239
/* Remove any dialog box */
240
static void gc_close_all_dialog() {
241 242
  gc_dialog_close();
  gc_help_stop();
243
  gc_config_stop();
244 245 246
  gc_about_stop();
  gc_selector_file_stop();
  gc_selector_images_stop();
247 248
}

249 250 251 252 253 254 255 256
/* Return the zoom factor we are currently using for our window
 *
 */
double gc_zoom_factor_get()
{
  return zoom_factor;
}

257 258 259 260 261 262 263 264 265 266 267 268
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);

269
  yratio=screen_height/(float)(BOARDHEIGHT);
270
  xratio=screen_width/(float)BOARDWIDTH;
271
  zoom_factor = MIN(xratio, yratio);
272 273 274
  g_message("The screen_width=%f screen_height=%f ratio=%f\n",
	    (double)screen_width, (double)screen_height, zoom_factor);

275 276 277
  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,
278
		 (screen_width-BOARDWIDTH*zoom_factor)/2,
279
		 (screen_height-BOARDHEIGHT*zoom_factor)/2);
280

281 282
  _expose_background_callback (drawing_area, NULL, NULL);

283 284 285
  return FALSE;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
286 287
static gint
board_widget_key_press_callback (GtkWidget   *widget,
288 289
				 GdkEventKey *event,
				 gpointer     client_data)
Bruno Coudoin's avatar
Bruno Coudoin committed
290
{
291
  int kv = event->keyval;
292

293 294
  if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_r)
					 || (event->keyval == GDK_R))) {
295
    g_message("Refreshing the canvas\n");
296
    goo_canvas_update(GOO_CANVAS(canvas));
297 298
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
299
  else if(event->state & GDK_CONTROL_MASK && ((event->keyval == GDK_q)
300
					 || (event->keyval == GDK_Q))) {
301
    gc_exit();
302 303
    return TRUE;
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
304 305 306 307 308 309 310 311 312
  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;
  }
313

Bruno Coudoin's avatar
Bruno Coudoin committed
314 315 316
  switch (event->keyval)
    {
    case GDK_Escape:
317
      gc_close_all_dialog();
318

319
      if (gc_board_get_current()->previous_board != NULL)
320
	gc_board_stop();
Bruno Coudoin's avatar
Bruno Coudoin committed
321
      return TRUE;
322
    case GDK_F5:
323
      g_message("Refreshing the canvas\n");
324
      goo_canvas_update(GOO_CANVAS(canvas));
325
      return TRUE;
326

327 328 329 330 331 332 333 334 335 336 337 338 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
    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;
369
    default:
370 371 372 373
      break;
    }

  /* pass through the IM context */
374
  if (gc_board_get_current() && (!gc_board_get_current()->disable_im_context))
375 376
    {
      if (gtk_im_context_filter_keypress (properties->context, event))
Bruno Coudoin's avatar
Bruno Coudoin committed
377
	{
378
	  g_message("%d key is handled by context", kv);
379
	  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
380 381 382
	}
    }

383
  g_message("%d key is NOT handled by context", kv);
384 385 386 387
  /* 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
   */
388
  if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
389
      && gc_board_get_current_board_plugin()->key_press)
390
    {
391
      return(gc_board_get_current_board_plugin()->key_press (event->keyval, NULL, NULL));
392
    }
393
  else if (gc_board_get_current_board_plugin()!=NULL
Bruno Coudoin's avatar
Bruno Coudoin committed
394
	   && gc_board_get_current_board_plugin()->ok &&
395 396 397
	   (event->keyval == GDK_KP_Enter ||
	    event->keyval == GDK_Return   ||
	    event->keyval == GDK_KP_Space))
398 399
    {
      /* Else we send the OK signal. */
400
      gc_board_get_current_board_plugin()->ok ();
401 402 403
      return TRUE;
    }

404
  /* Event not handled; try parent item */
Bruno Coudoin's avatar
Bruno Coudoin committed
405 406 407
  return FALSE;
};

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
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 :
426
      cursor_pixbuf = gc_skin_pixmap_load("cursors/default.png");
427 428
      break;
    case GCOMPRIS_LINE_CURSOR :
429
      cursor_pixbuf = gc_skin_pixmap_load("cursors/line.png");
430 431
      break;
    case GCOMPRIS_RECT_CURSOR :
432
      cursor_pixbuf = gc_skin_pixmap_load("cursors/rect.png");
433 434
      break;
    case GCOMPRIS_FILLRECT_CURSOR :
435
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fillrect.png");
436 437
      break;
    case GCOMPRIS_CIRCLE_CURSOR :
438
      cursor_pixbuf = gc_skin_pixmap_load("cursors/circle.png");
439 440
      break;
    case GCOMPRIS_FILLCIRCLE_CURSOR :
441
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fillcircle.png");
442 443
      break;
    case GCOMPRIS_FILL_CURSOR :
444
      cursor_pixbuf = gc_skin_pixmap_load("cursors/fill.png");
445 446
      break;
    case GCOMPRIS_DEL_CURSOR :
447
      cursor_pixbuf = gc_skin_pixmap_load("cursors/del.png");
448 449
      break;
    case GCOMPRIS_SELECT_CURSOR :
450
      cursor_pixbuf = gc_skin_pixmap_load("cursors/select.png");
451 452 453 454 455 456 457 458
      break;
    default :
      return;
      break;
    }

    if(cursor_pixbuf)
      {
459 460
	cursor = gdk_cursor_new_from_pixbuf(gdk_display_get_default(),
					    cursor_pixbuf, 0, 0);
461 462 463 464 465 466 467
	gdk_window_set_cursor(window->window, cursor);
	gdk_cursor_unref(cursor);
	gdk_pixbuf_unref(cursor_pixbuf);
      }
  }
}

Bruno Coudoin's avatar
Bruno Coudoin committed
468 469 470
/**
 * Return the main canvas we run in
 */
471
GooCanvas *gc_get_canvas()
Bruno Coudoin's avatar
Bruno Coudoin committed
472
{
473
  return GOO_CANVAS (canvas);
Bruno Coudoin's avatar
Bruno Coudoin committed
474 475
}

476
GtkWidget *gc_get_window()
477 478 479 480
{
  return window;
}

Bruno Coudoin's avatar
Bruno Coudoin committed
481 482
static void
_set_svg_background(GooCanvasItem *parent, gchar *file)
Bruno Coudoin's avatar
Bruno Coudoin committed
483
{
Bruno Coudoin's avatar
Bruno Coudoin committed
484
  RsvgHandle *rsvg_handle;
Bruno Coudoin's avatar
Bruno Coudoin committed
485

486
  rsvg_handle = gc_rsvg_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
487 488 489

  if(backgroundsvgimg)
    g_object_set(backgroundsvgimg,
490
		 "svg-handle", rsvg_handle,
Bruno Coudoin's avatar
Bruno Coudoin committed
491 492
		 NULL);
  else
493
    backgroundsvgimg = goo_canvas_svg_new (parent,
Bruno Coudoin's avatar
Bruno Coudoin committed
494 495 496 497 498 499 500 501 502 503 504 505
					 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;

506
  background_pixmap = gc_pixmap_load (file);
Bruno Coudoin's avatar
Bruno Coudoin committed
507 508

  if(backgroundimg)
509 510 511
    g_object_set(backgroundimg,
		 "pixbuf", background_pixmap,
		 NULL);
512
  else
513 514 515 516 517 518 519 520
    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
521 522 523

  gdk_pixbuf_unref(background_pixmap);

Bruno Coudoin's avatar
Bruno Coudoin committed
524 525
}

526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
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
542 543 544 545 546 547 548 549 550 551 552
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);
553 554

      backgroundimg = NULL;
Bruno Coudoin's avatar
Bruno Coudoin committed
555 556 557 558
      _set_svg_background(parent, file);
    }
  else
    {
559
      _clear_svg_background();
Bruno Coudoin's avatar
Bruno Coudoin committed
560 561
      _set_pixmap_background(parent, file);
    }
Bruno Coudoin's avatar
Bruno Coudoin committed
562 563
}

564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
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);
}

587 588 589
/* Redraw the black background
 */
static gboolean
590 591
_expose_background_callback (GtkWidget *widget,
			     GdkEventExpose *event, gpointer data)
592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607
{
  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;
}

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
/*
 * 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;
}

635 636
static void
init_background()
Bruno Coudoin's avatar
Bruno Coudoin committed
637
{
638
  drawing_area = gtk_drawing_area_new ();
639
  gtk_widget_set_size_request (drawing_area, BOARDWIDTH, BOARDHEIGHT);
640 641
  g_signal_connect (G_OBJECT (drawing_area), "expose_event",
		    G_CALLBACK (_expose_background_callback), NULL);
642
  /* Create a vertical box in which I put first the play board area, then the button bar */
643 644
  fixed = gtk_fixed_new ();
  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(fixed));
645

646
  gtk_fixed_put (GTK_FIXED(fixed), GTK_WIDGET(drawing_area), 0, 0);
647 648 649
  gtk_fixed_put (GTK_FIXED(fixed), GTK_WIDGET(canvas), 0, 0);

  gtk_widget_show (GTK_WIDGET(fixed));
650 651
  gtk_widget_show (GTK_WIDGET(canvas));

652
  gtk_widget_set_usize (GTK_WIDGET(canvas), BOARDWIDTH, BOARDHEIGHT);
653 654 655 656 657
  goo_canvas_set_bounds (GOO_CANVAS(canvas),
			 0, 0,
			 BOARDWIDTH,
			 BOARDHEIGHT);

658
}
Bruno Coudoin's avatar
Bruno Coudoin committed
659

660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 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
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;
}

707 708
static void setup_window ()
{
709
  GcomprisBoard *board_to_start;
710 711

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

713 714 715 716
  /*
   * Set an icon for gcompris
   * ------------------------
   */
717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
  {
    GdkPixbuf *icon_pixbuf = NULL;
    gchar *iconfile = gc_file_find_absolute("%s/%s",
					    properties->system_icon_dir, "gcompris.png",
					    NULL);
    if(iconfile)
      {
	icon_pixbuf = gc_pixmap_load(iconfile);
	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'");
735

736
  }
737

738
  gtk_window_set_title(GTK_WINDOW (window), "GCompris");
739

740 741 742 743 744 745 746
  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;
747
  hints.min_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
748
  hints.max_aspect = (float)BOARDWIDTH/BOARDHEIGHT;
749 750 751 752 753
  gtk_window_set_geometry_hints (GTK_WINDOW (window),
				 NULL,
				 &hints,
				 GDK_HINT_RESIZE_INC |
				 GDK_HINT_MIN_SIZE |
754 755
				 GDK_HINT_BASE_SIZE |
				 GDK_HINT_ASPECT);
756

757 758 759 760
  /*
   * Set the main window
   * -------------------
   */
761

762
  gtk_window_set_default_size(GTK_WINDOW(window), BOARDWIDTH, BOARDHEIGHT);
763
  gtk_window_set_wmclass(GTK_WINDOW(window), "gcompris", "GCompris");
Bruno Coudoin's avatar
Bruno Coudoin committed
764

765 766 767
  g_signal_connect (GTK_OBJECT (window), "realize",
		    G_CALLBACK (_realize_callback), NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
768
  gtk_widget_realize (window);
Bruno Coudoin's avatar
Bruno Coudoin committed
769

Bruno Coudoin's avatar
Bruno Coudoin committed
770 771 772
  gtk_signal_connect (GTK_OBJECT (window), "delete_event",
		      GTK_SIGNAL_FUNC (quit_cb), NULL);

773 774
  gtk_signal_connect (GTK_OBJECT (window), "map_event",
		      GTK_SIGNAL_FUNC (map_cb), NULL);
Bruno Coudoin's avatar
Bruno Coudoin committed
775

776
  gtk_signal_connect (GTK_OBJECT (window), "configure_event",
777
		      GTK_SIGNAL_FUNC (_gc_configure_event_callback), NULL);
778

779
  // Set the cursor
780
  gc_cursor_set(GCOMPRIS_DEFAULT_CURSOR);
781

782
  canvas     = goo_canvas_new();
Bruno Coudoin's avatar
Bruno Coudoin committed
783

784 785 786 787
  g_object_set (G_OBJECT(goo_canvas_get_root_item(GOO_CANVAS(canvas))),
		"can-focus", TRUE,
		NULL);

Bruno Coudoin's avatar
Bruno Coudoin committed
788

789 790
  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
791 792 793 794 795 796
  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);
797

798
  gc_im_init(window);
799

800
  gc_board_init();
801

802
  /* Load all the menu once */
803
  gc_menu_load();
804

805
  /* Save the root_menu */
806
  properties->menu_board = gc_menu_section_get(properties->root_menu);
807

808
  /* Run the bar */
809
  gc_bar_start(GOO_CANVAS(canvas));
810 811 812

  init_background();

813 814
  board_to_start = get_board_to_start();

815
  if(!board_to_start) {
816 817 818
    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);
819
  } else if(!gc_board_check_file(board_to_start)) {
820 821 822
    gchar *tmpstr= g_strdup_printf("Couldn't find the board menu, or plugin execution error");
    gc_dialog(tmpstr, NULL);
    g_free(tmpstr);
823
  } else {
824
    g_message("Fine, we got the gcomprisBoardMenu, xml boards parsing went fine");
825 826
    if(!display_activation_dialog())
      gc_board_play(board_to_start);
827
  }
828

829 830 831

}

832
#ifdef STATIC_MODULE
833 834
/** Display the activation dialog for the windows version
 *
835
 * return TRUE is the dialog is display, FALSE instead.
836
 */
837
int
838 839
display_activation_dialog()
{
840
  int board_count = 0;
841
  int gc_board_number_in_demo = 0;
842
  GList *list;
843 844
  guint  key_is_valid = 0;

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

Bruno Coudoin's avatar
Bruno Coudoin committed
847
  if(key_is_valid == 1)
848
    return FALSE;
849 850 851 852 853 854

  /* 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 &&
855 856 857 858 859 860 861
	  strncmp(board->section, "/experimental", 13) != 0)
	{
	  board_count++;
	  gc_board_check_file(board);
	  if(board->plugin)
	      gc_board_number_in_demo++;
	}
862 863
    }

864 865
  /* Entry area */
  widget_activation_entry = (GtkEntry *)gtk_entry_new();
Bruno Coudoin's avatar
Bruno Coudoin committed
866
  gtk_entry_set_max_length(widget_activation_entry, 6);
867
  activation_item = \
868
    goo_canvas_item_new (goo_canvas_get_root_item(canvas),
869
			   goo_canvas_widget_get_type (),
870 871 872 873 874 875 876 877 878 879 880 881 882 883 884
			   "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");

885
  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>"),
886 887 888
			      gc_board_number_in_demo, board_count);
  gc_dialog(msg, activation_done);
  g_free(msg);
889 890

  return TRUE;
Bruno Coudoin's avatar
Bruno Coudoin committed
891 892
}

893 894 895 896 897 898 899
/**
 * 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
900
#ifdef  DISABLE_ACTIVATION_CODE
Bruno Coudoin's avatar
Bruno Coudoin committed
901
  return 1;
Bruno Coudoin's avatar
Bruno Coudoin committed
902
#else
903 904 905 906 907 908 909 910 911 912 913 914 915
  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
916
      crc1 = (crc1 ^ code[i]) & 0x07;
917 918 919 920
    }
  value = value >> 3;
  crc1 = 0x30 | crc1;
  crc2 = 0x30 | (code[2] ^ code[3]);
921

Bruno Coudoin's avatar
Bruno Coudoin committed
922 923 924 925 926
  if(crc1 != code[4])
    return(-1);

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

Bruno Coudoin's avatar
Bruno Coudoin committed
928
  codeddate[3] = 0x30 | (value & 0x000F);
929
  value = value >> 4;
930

Bruno Coudoin's avatar
Bruno Coudoin committed
931
  codeddate[2] = 0x30 | (value & 0x0001);
932
  value = value >> 1;
933

Bruno Coudoin's avatar
Bruno Coudoin committed
934
  codeddate[1] = 0x30 | (value & 0x000F);
935
  value = value >> 4;
936

Bruno Coudoin's avatar
Bruno Coudoin committed
937
  codeddate[0] = 0x30 | (value & 0x0003);
938 939 940 941 942 943
  codeddate[4] = '\0';

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

947 948 949 950
/* Check the activation code
 *
 */
static void
951 952
activation_enter_callback( GtkWidget *entry,
			   GtkWidget *notused )
953
{
954
  switch(gc_activation_check((char *)gtk_entry_get_text(GTK_ENTRY(entry))))
955
    {
956
    case 1:
Bruno Coudoin's avatar
Bruno Coudoin committed
957 958
      gc_prop_get()->key = strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
      gc_prop_save(properties);
959 960 961 962 963 964 965 966
      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;
967 968 969 970 971 972 973 974 975
    }
}

/* Callback for the activation dialog
 *
 */
static void
activation_done()
{
976 977 978 979
  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))
    {
980
      activation_enter_callback(GTK_WIDGET(widget_activation_entry), NULL);
981
    }
982

983
  gc_board_play( get_board_to_start());
984 985 986 987 988 989 990
  gtk_object_destroy (GTK_OBJECT(activation_item));
}
#endif

/** Call me to end an activity
 *
 */
991
void gc_board_end()
Bruno Coudoin's avatar
Bruno Coudoin committed
992
{
993
  if (gc_board_get_current()->previous_board) {
994
    /* Run the previous board */
995
    gc_board_play(gc_board_get_current()->previous_board);
996
  }
Bruno Coudoin's avatar
Bruno Coudoin committed
997 998
}

999 1000 1001 1002
/** \brief toggle full screen mode
 *
 *
 */
1003
void gc_fullscreen_set(gboolean state)
1004
{
Bruno Coudoin's avatar
Bruno Coudoin committed
1005
  fullscreen = state;
1006 1007 1008
  if(state)
    {
      gdk_window_set_decorations (window->window, 0);
1009
      gtk_window_fullscreen (GTK_WINDOW(window));
1010
      gtk_widget_set_uposition (window, 0, 0);
1011 1012
    }
  else
1013
    {
1014
      gdk_window_set_decorations (window->window, GDK_DECOR_ALL);
1015
      gtk_window_unfullscreen (GTK_WINDOW(window));
Bruno Coudoin's avatar
Bruno Coudoin committed
1016 1017 1018

      /* Mandatory or on windows we get iconified */
      gtk_window_deiconify (GTK_WINDOW(window));
1019 1020 1021 1022
    }

}

1023
/* Use these instead of the goo_canvas ones for proper fullscreen mousegrab
1024
   handling. */
1025 1026
int gc_canvas_item_grab (GooCanvasItem *item, unsigned int event_mask,
			 GdkCursor *cursor, guint32 etime)
1027 1028
{
  int retval;
1029

1030 1031
  retval = goo_canvas_pointer_grab(goo_canvas_item_get_canvas(item), item,
				   event_mask, cursor, etime);
1032 1033
  if (retval != GDK_GRAB_SUCCESS)
    return retval;
1034

1035 1036 1037
  return retval;
}

1038
void gc_canvas_item_ungrab (GooCanvasItem *item, guint32 etime)
1039
{
1040
  goo_canvas_pointer_ungrab(goo_canvas_item_get_canvas(item), item, etime);
1041 1042
}

1043
static void cleanup()
Bruno Coudoin's avatar
Bruno Coudoin committed
1044
{
1045 1046 1047 1048
  /* Do not loopback in exit */
  signal(SIGINT,  NULL);
  signal(SIGSEGV, NULL);

1049
  single_instance_release(); /* Must be done before property destroy */
1050
  gc_board_stop();
1051
  gc_db_exit();
1052
  gc_fullscreen_set(FALSE);
1053
  gc_menu_destroy();
1054
  gc_prop_destroy(gc_prop_get());
1055
}
1056

1057
void gc_exit()
1058 1059
{
  g_signal_emit_by_name(G_OBJECT(window), "delete_event");
1060
}
Bruno Coudoin's avatar
Bruno Coudoin committed
1061

1062 1063
static void quit_cb (GtkWidget *widget, gpointer data)
{
1064 1065 1066 1067

#ifdef DMALLOC
  dmalloc_shutdown();
#endif
1068 1069
  cleanup();
  gtk_main_quit();
1070

1071 1072 1073 1074 1075
  /*
   * 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
1076 1077
}

1078 1079 1080 1081 1082 1083 1084 1085
/*
 * We want GCompris to be set as fullscreen the later possible
 *
 */
static void map_cb (GtkWidget *widget, gpointer data)
{
  if(is_mapped == FALSE)
    {
1086
      gc_fullscreen_set(properties->fullscreen);
Bruno Coudoin's avatar
Bruno Coudoin committed
1087
      is_mapped = TRUE;
1088
    }
1089
  g_message("gcompris window is now mapped");
1090 1091
}

1092
/*
1093
 * Process the cleanup of the child (no zombies)
1094 1095
 * ---------------------------------------------
 */
1096
void gc_terminate(int signum)
1097 1098
{

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

1101
  gc_exit();
1102

1103 1104
}

Bruno Coudoin's avatar
Bruno Coudoin committed
1105 1106
static void load_properties ()
{
1107
  properties = gc_prop_new ();
1108 1109 1110 1111 1112

  /* Initialize the binary relocation API
   *  http://autopackage.org/docs/binreloc/
   */
  if(gbr_init (NULL))
1113
    g_message("Binary relocation enabled");
1114
  else
1115
    g_message("Binary relocation disabled");
1116

1117 1118
  /* usefull for OSX bundle app */
  /* FIXME exec_prefix should be put in properties */
Bruno Coudoin's avatar
Bruno Coudoin committed
1119
  /* usefull for OSX bundle app */
1120 1121 1122
#ifdef NSBUNDLE
  exec_prefix = gcompris_nsbundle_resource ();
#else
Bruno Coudoin's avatar
Bruno Coudoin committed
1123
  exec_prefix = gbr_find_exe_dir(NULL);
1124
#endif
Bruno Coudoin's avatar
Bruno Coudoin committed
1125
  g_warning("exec_prefix %s\n", (exec_prefix==NULL ? "NONE" : exec_prefix));
1126

1127 1128 1129 1130 1131
  {
    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);
1132
    properties->package_skin_dir = g_strconcat(pkg_data_dir, "/gcompris/boards/skins", NULL);
1133 1134 1135 1136 1137 1138 1139 1140 1141
    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);
  }
1142

1143
  /* Display the directory value we have */
1144
  printf("package_data_dir         = %s\n", properties->package_data_dir);
1145
  printf("package_skin_dir         = %s\n", properties->package_skin_dir);
1146
  printf("package_menu_dir         = %s\n", properties->menu_dir);
1147 1148 1149
  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
1150 1151
}

1152
GcomprisProperties *gc_prop_get ()
1153 1154 1155 1156
{
  return (properties);
}

1157 1158 1159
/* Return the database file name
 * Must be called after properties is initialised
 */
1160
gchar *gc_db_get_filename ()
1161 1162 1163 1164 1165 1166
{
  g_assert(properties!=NULL);

  return (properties->database);
}

Bruno Coudoin's avatar
Bruno Coudoin committed
1167 1168 1169 1170
/*
 * This returns the locale for which text must be displayed
 *
 */
1171
const gchar *gc_locale_get()
Bruno Coudoin's avatar
Bruno Coudoin committed
1172
{
1173
  const gchar *locale;
1174

1175
  /* First check locale got overrided by the user */
1176 1177
  if(gc_locale != NULL)
    return(gc_locale);
1178

1179
  locale = g_getenv("LC_ALL");
1180
  if(locale == NULL)
1181
    locale = g_getenv("LC_CTYPE");
1182
  if(locale == NULL)
1183
    locale = g_getenv("LANG");
1184 1185 1186 1187

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

1188
  return("en_US.UTF-8");
Bruno Coudoin's avatar
Bruno Coudoin committed
1189 1190
}

1191 1192 1193 1194 1195
/*
 * This return the user default locale like it was at program
 * startup before we started changing the locale
 *
 */
1196
char *gc_locale_get_user_default()
1197
{
1198
  return gc_user_default_locale;
1199 1200
}

1201 1202 1203 1204
/*
 * This set the locale for which text must be displayed
 *
 */
1205 1206
void
gc_locale_set(gchar *locale)
1207 1208
{

1209 1210 1211
  if(!locale)
    return;

1212
  g_message("gc_locale_set '%s'\n", locale);
1213 1214
  if(gc_locale != NULL)
    g_free(gc_locale);
1215

1216 1217
#if defined WIN32
  /* On windows, it always works */
1218
  gc_locale = g_strdup(locale);
1219 1220 1221
  setlocale(LC_MESSAGES, locale);
  setlocale(LC_ALL, locale);
#else
1222
  gc_locale = g_strdup(setlocale(LC_CTYPE, locale));
1223 1224
  if (!gc_locale)
    gc_locale = g_strdup(locale);
Bruno Coudoin's avatar