[tracker/collation-gconf-locale: 1/20] libtracker-common: Locale retrieval from gconf implemented



commit 37321f7310bb7e81e66af6d48a763b3a28bde1ab
Author: Aleksander Morgado <aleksander lanedo com>
Date:   Wed Sep 1 18:49:28 2010 +0200

    libtracker-common: Locale retrieval from gconf implemented

 configure.ac                           |   17 ++
 src/libtracker-common/Makefile.am      |    6 +-
 src/libtracker-common/tracker-common.h |    1 +
 src/libtracker-common/tracker-locale.c |  409 ++++++++++++++++++++++++++++++++
 src/libtracker-common/tracker-locale.h |   64 +++++
 5 files changed, 495 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 5d96031..ade5661 100644
--- a/configure.ac
+++ b/configure.ac
@@ -606,6 +606,23 @@ AC_ARG_ENABLE(maemo,
 
 AM_CONDITIONAL(HAVE_MAEMO, test "x$enable_maemo" = "xyes")
 
+if test "x$enable_maemo" = "xyes" ; then
+
+   AC_DEFINE(HAVE_MAEMO, 1, [Define if we enable Maemo specific features])
+
+   PKG_CHECK_MODULES(GCONF,
+                     [gconf-2.0],
+                     [have_gconf=yes],
+                     [have_gconf=no])
+
+   LIBTRACKER_COMMON_CFLAGS="$LIBTRACKER_COMMON_CFLAGS $GCONF_CFLAGS"
+   LIBTRACKER_COMMON_LIBS="$LIBTRACKER_COMMON_LIBS $GCONF_LIBS"
+
+   if test "x$have_gconf" != "xyes"; then
+      AC_MSG_ERROR([Couldn't find gconf, needed when enabling maemo support])
+   fi
+fi
+
 ####################################################################
 # Check for SQLite
 ####################################################################
diff --git a/src/libtracker-common/Makefile.am b/src/libtracker-common/Makefile.am
index 4020384..b230a58 100644
--- a/src/libtracker-common/Makefile.am
+++ b/src/libtracker-common/Makefile.am
@@ -43,7 +43,8 @@ libtracker_common_la_SOURCES =                         \
 	tracker-log.c                                  \
 	tracker-type-utils.c                           \
 	tracker-utils.c                                \
-	tracker-crc32.c
+	tracker-crc32.c                                \
+	tracker-locale.c
 
 noinst_HEADERS =                                       \
 	$(power_headers)                               \
@@ -59,7 +60,8 @@ noinst_HEADERS =                                       \
 	tracker-ontologies.h                           \
 	tracker-type-utils.h                           \
 	tracker-utils.h                                \
-	tracker-crc32.h
+	tracker-crc32.h                                \
+	tracker-locale.h
 
 if HAVE_TRACKER_FTS
 libtracker_common_la_SOURCES +=                        \
diff --git a/src/libtracker-common/tracker-common.h b/src/libtracker-common/tracker-common.h
index fbd0550..7fcfc72 100644
--- a/src/libtracker-common/tracker-common.h
+++ b/src/libtracker-common/tracker-common.h
@@ -41,6 +41,7 @@
 #include "tracker-power.h"
 #include "tracker-type-utils.h"
 #include "tracker-utils.h"
+#include "tracker-locale.h"
 
 #undef __LIBTRACKER_COMMON_INSIDE__
 
diff --git a/src/libtracker-common/tracker-locale.c b/src/libtracker-common/tracker-locale.c
new file mode 100644
index 0000000..9921418
--- /dev/null
+++ b/src/libtracker-common/tracker-locale.c
@@ -0,0 +1,409 @@
+/*
+ * Copyright (C) 2010 Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#include "config.h"
+
+#include <locale.h>
+#include <string.h>
+
+#include <glib.h>
+
+#include "tracker-locale.h"
+
+#ifdef HAVE_MAEMO
+#include <gconf/gconf-client.h>
+#endif /* HAVE_MAEMO */
+
+#ifdef HAVE_MAEMO
+/* Mutex to sync access to the current locale values */
+static GStaticMutex locales_mutex = G_STATIC_MUTEX_INIT;
+static GStaticMutex subscribers_mutex = G_STATIC_MUTEX_INIT;
+#define LOCK_LOCALES g_static_mutex_lock (&locales_mutex)
+#define UNLOCK_LOCALES g_static_mutex_unlock (&locales_mutex)
+#define LOCK_SUBSCRIBERS g_static_mutex_lock (&subscribers_mutex)
+#define UNLOCK_SUBSCRIBERS g_static_mutex_unlock (&subscribers_mutex)
+#else
+/* If not using gconf locales, no need to acquire/release any lock */
+#define LOCK_LOCALES
+#define UNLOCK_LOCALES
+#endif /* HAVE_MAEMO */
+
+/* Current locales in use. They will be stored in heap and available throughout
+ * the whole program execution, so will be reported as still reachable by Valgrind.
+ */
+static gchar *current_locales[TRACKER_LOCALE_LAST];
+
+static gchar *locale_names[TRACKER_LOCALE_LAST] = {
+	"TRACKER_LOCALE_LANGUAGE",
+	"TRACKER_LOCALE_TIME",
+	"TRACKER_LOCALE_COLLATE",
+	"TRACKER_LOCALE_NUMERIC",
+	"TRACKER_LOCALE_MONETARY"
+};
+
+/* Already initialized? */
+static gboolean initialized;
+
+#ifdef HAVE_MAEMO
+
+/* If this envvar is set, even if we have meegotouch locales in gconf,
+ * we'll still use envvars */
+#define DISABLE_MEEGOTOUCH_LOCALE_ENV "DISABLE_MEEGOTOUCH_LOCALE"
+
+/* Base dir for all gconf locale values */
+#define MEEGOTOUCH_LOCALE_DIR "/meegotouch/i18n"
+
+/* gconf keys for tracker locales, as defined in:
+ * http://apidocs.meego.com/mtf/i18n.html
+ */
+static const gchar *gconf_locales[TRACKER_LOCALE_LAST] = {
+	MEEGOTOUCH_LOCALE_DIR "/language",
+	MEEGOTOUCH_LOCALE_DIR "/lc_time",
+	MEEGOTOUCH_LOCALE_DIR "/lc_collate",
+	MEEGOTOUCH_LOCALE_DIR "/lc_numeric",
+	MEEGOTOUCH_LOCALE_DIR "/lc_monetary"
+};
+
+/* Structure to hold the notification data of each subscriber */
+typedef struct {
+	TrackerLocaleID id;
+	TrackerLocaleNotifyFunc func;
+	gpointer user_data;
+	GFreeFunc destroy_notify;
+} TrackerLocaleNotification;
+
+/* List of subscribers which want to get notified of locale changes */
+static GSList *subscribers;
+
+/* The gconf client, which will be created once at initialization and
+ * the reference will never be destroyed, so will be reported as still
+ * reachable by Valgrind */
+static GConfClient *client;
+
+
+static void
+tracker_locale_set (TrackerLocaleID  id,
+                    const gchar     *value)
+{
+	if (current_locales[id]) {
+		g_debug ("Locale '%s' was changed from '%s' to '%s'",
+		         locale_names[id],
+		         current_locales[id],
+		         value);
+		g_free (current_locales[id]);
+	} else {
+		g_debug ("Locale '%s' was set to '%s'",
+		         locale_names[id],
+		         value);
+	}
+
+	/* Store the new one */
+	current_locales[id] = g_strdup (value);
+
+	/* And also set the new one in the corresponding envvar */
+	switch (id) {
+	case TRACKER_LOCALE_LANGUAGE:
+		g_setenv ("LANG", value, TRUE);
+		break;
+	case TRACKER_LOCALE_TIME:
+		setlocale (LC_TIME, value);
+		break;
+	case TRACKER_LOCALE_COLLATE:
+		setlocale (LC_COLLATE, value);
+		break;
+	case TRACKER_LOCALE_NUMERIC:
+		setlocale (LC_NUMERIC, value);
+		break;
+	case TRACKER_LOCALE_MONETARY:
+		setlocale (LC_MONETARY, value);
+		break;
+	case TRACKER_LOCALE_LAST:
+		/* Make compiler happy */
+		g_warn_if_reached ();
+		break;
+	}
+}
+
+static void
+tracker_locale_gconf_notify_cb (GConfClient *client,
+                                guint cnxn_id,
+                                GConfEntry *entry,
+                                gpointer user_data)
+{
+	guint i;
+	GConfValue *value;
+	GSList *li;
+	const gchar *string_value;
+
+	/* Find the proper locale to change */
+	for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
+		if (strcmp (gconf_locales[i], gconf_entry_get_key (entry)) == 0) {
+			break;
+		}
+	}
+
+	/* Oh, not found? */
+	if (i == TRACKER_LOCALE_LAST) {
+		g_debug ("Skipping change on gconf key '%s' as not really needed",
+		         gconf_entry_get_key (entry));
+		return;
+	}
+
+	/* Ensure a proper value was set */
+	value = gconf_entry_get_value (entry);
+	if (!value) {
+		g_warning ("Locale value for '%s' cannot be NULL, not changing %s",
+		           gconf_locales[i],
+		           locale_names[i]);
+		return;
+	}
+
+	/* It must be a string */
+	if (value->type != GCONF_VALUE_STRING) {
+		g_warning ("Locale value for '%s' must be a string, not changing %s",
+		           gconf_locales[i],
+		           locale_names[i]);
+		return;
+	}
+
+	string_value = gconf_value_get_string (value);
+
+	/* String must have a length > 0 */
+	if (!string_value ||
+	    strlen (string_value) == 0) {
+		g_warning ("Locale value for '%s' must not be empty, not changing %s",
+		           gconf_locales[i],
+		           locale_names[i]);
+		return;
+	}
+
+	/* Protect the locale change with the lock */
+	LOCK_LOCALES;
+	tracker_locale_set (i, gconf_value_get_string (value));
+	UNLOCK_LOCALES;
+
+	/* Now, if any subscriber, notify the locale change.
+	 * NOTE!!!! The callback MUST NOT perform any action
+	 * that may change the list of subscribers, or the
+	 * program will get locked. */
+	LOCK_SUBSCRIBERS;
+	for (li = subscribers; li; li = g_slist_next (li)) {
+		TrackerLocaleNotification *data = li->data;
+
+		if (i == data->id) {
+			g_debug ("Notifying locale '%s' change to subscriber '%p'",
+			         locale_names[i],
+			         data);
+			data->func (i, data->user_data);
+		}
+	}
+	UNLOCK_SUBSCRIBERS;
+}
+
+#endif /* HAVE_MAEMO */
+
+static void
+tracker_locale_init (void)
+{
+	guint i;
+
+#ifdef HAVE_MAEMO
+	if (g_getenv (DISABLE_MEEGOTOUCH_LOCALE_ENV)) {
+		g_message ("Retrieving locale from GConf is DISABLED");
+	} else {
+		GError *error = NULL;
+
+		g_message ("Retrieving locale from GConf is ENABLED");
+
+		/* Get default gconf client to query the locale values */
+		client = gconf_client_get_default ();
+
+		/* We want to be notified when locales are changed in gconf */
+		gconf_client_add_dir (client,
+		                      MEEGOTOUCH_LOCALE_DIR,
+		                      GCONF_CLIENT_PRELOAD_ONELEVEL,
+		                      &error);
+		if (error) {
+			g_warning ("Cannot add dir '%s' in gconf client: '%s'",
+			           MEEGOTOUCH_LOCALE_DIR,
+			           error->message);
+			g_clear_error (&error);
+		} else {
+			/* Request notifications */
+			gconf_client_notify_add (client,
+			                         MEEGOTOUCH_LOCALE_DIR,
+			                         tracker_locale_gconf_notify_cb,
+			                         NULL,
+			                         NULL,
+			                         &error);
+			if (error) {
+				g_warning ("Cannot request notifications under dir '%s' in "
+				           "gconf client: '%s'",
+				           MEEGOTOUCH_LOCALE_DIR,
+				           error->message);
+				g_clear_error (&error);
+			}
+		}
+
+		/* And initialize all, should have been all preloaded in the
+		 * client already */
+		for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
+			GConfValue *val;
+
+			/* Get the gconf key, if any */
+			val = gconf_client_get (client,
+			                        gconf_locales[i],
+			                        &error);
+			if (!val) {
+				g_warning ("Couldn't get value for key '%s'"
+				           " from gconf: '%s'"
+				           " Defaulting to environment locale.",
+				           gconf_locales[i],
+				           error ? error->message : "no such key");
+				g_clear_error (&error);
+			} else if (val->type != GCONF_VALUE_STRING) {
+				g_warning ("Wrong type for '%s' key in gconf..."
+				           " Defaulting to environment locale.",
+				           gconf_locales[i]);
+				gconf_value_free (val);
+			} else {
+				/* Set the new locale */
+				tracker_locale_set (i, gconf_value_get_string (val));
+				gconf_value_free (val);
+			}
+		}
+	}
+#endif /* HAVE_MAEMO */
+
+	/* Initialize those not retrieved from gconf, or if not in maemo */
+	for (i = 0; i < TRACKER_LOCALE_LAST; i++) {
+		if (!current_locales[i]) {
+			const gchar *env_locale;
+
+			switch (i) {
+			case TRACKER_LOCALE_LANGUAGE:
+				env_locale = g_getenv ("LANG");
+				break;
+			case TRACKER_LOCALE_TIME:
+				env_locale = setlocale (LC_TIME, NULL);
+				break;
+			case TRACKER_LOCALE_COLLATE:
+				env_locale = setlocale (LC_COLLATE, NULL);
+				break;
+			case TRACKER_LOCALE_NUMERIC:
+				env_locale = setlocale (LC_NUMERIC, NULL);
+				break;
+			case TRACKER_LOCALE_MONETARY:
+				env_locale = setlocale (LC_MONETARY, NULL);
+				break;
+			case TRACKER_LOCALE_LAST:
+				/* Make compiler happy. The for loop avoids
+				 * this from happening. */
+				break;
+			}
+
+			if (!env_locale) {
+				g_warning ("Locale '%d' is not set, defaulting to C locale", i);
+				tracker_locale_set (i, "C");
+			} else {
+				tracker_locale_set (i, env_locale);
+			}
+		}
+	}
+
+	/* So we're initialized */
+	initialized = TRUE;
+}
+
+gchar *
+tracker_locale_get (TrackerLocaleID id)
+{
+	gchar *locale;
+
+	/* Lock even before checking if initialized, so that initialization is
+	 * not done twice */
+	LOCK_LOCALES;
+
+	/* Initialize if not already done */
+	if (!initialized)
+		tracker_locale_init ();
+
+	/* Always return a duplicated string, as the locale may change at any
+	 * moment */
+	locale = g_strdup (current_locales[id]);
+
+	UNLOCK_LOCALES;
+
+	return locale;
+}
+
+gpointer
+tracker_locale_notify_add (TrackerLocaleID         id,
+                           TrackerLocaleNotifyFunc func,
+                           gpointer                user_data,
+                           GFreeFunc               destroy_notify)
+{
+#ifdef HAVE_MAEMO
+	TrackerLocaleNotification *data;
+
+	g_assert (func);
+
+	data = g_slice_new (TrackerLocaleNotification);
+	data->id = id;
+	data->func = func;
+	data->user_data = user_data;
+	data->destroy_notify = destroy_notify;
+
+	/* Lock list, we cannot! use the same lock as for locales here... */
+	LOCK_SUBSCRIBERS;
+	subscribers = g_slist_prepend (subscribers, data);
+	UNLOCK_SUBSCRIBERS;
+
+	return data;
+#else
+	/* If not using gconf locales, this is a no-op... */
+	return NULL;
+#endif /* HAVE_MAEMO */
+}
+
+void
+tracker_locale_notify_remove (gpointer notification_id)
+{
+#ifdef HAVE_MAEMO
+	GSList *li;
+
+	LOCK_SUBSCRIBERS;
+	li = g_slist_find (subscribers, notification_id);
+	if (li) {
+		TrackerLocaleNotification *data = li->data;
+
+		/* Remove item from list of subscribers */
+		subscribers = g_slist_delete_link (subscribers, li);
+
+		/* Call the provided destroy_notify if any. */
+		if (data->destroy_notify)
+			data->destroy_notify (data->user_data);
+		/* And fully dispose the notification data */
+		g_slice_free (TrackerLocaleNotification, data);
+	}
+	UNLOCK_SUBSCRIBERS;
+#else
+	/* If not using gconf locales, this is a no-op... */
+#endif /* HAVE_MAEMO */
+}
diff --git a/src/libtracker-common/tracker-locale.h b/src/libtracker-common/tracker-locale.h
new file mode 100644
index 0000000..49dac0b
--- /dev/null
+++ b/src/libtracker-common/tracker-locale.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Nokia <ivan frade nokia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA  02110-1301, USA.
+ */
+
+#ifndef __LIBTRACKER_COMMON_LOCALE_H__
+#define __LIBTRACKER_COMMON_LOCALE_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+#if !defined (__LIBTRACKER_COMMON_INSIDE__) && !defined (TRACKER_COMPILATION)
+#error "only <libtracker-common/tracker-common.h> must be included directly."
+#endif
+
+/* Type of locales supported in tracker */
+typedef enum {
+	TRACKER_LOCALE_LANGUAGE,
+	TRACKER_LOCALE_TIME,
+	TRACKER_LOCALE_COLLATE,
+	TRACKER_LOCALE_NUMERIC,
+	TRACKER_LOCALE_MONETARY,
+	TRACKER_LOCALE_LAST
+} TrackerLocaleID;
+
+/* Callback for the notification of locale changes */
+typedef void (* TrackerLocaleNotifyFunc) (TrackerLocaleID id,
+                                          gpointer user_data);
+
+/* Get the current locale of the given type.
+ * Note that it returns a newly-allocated string which should be g_free()-ed
+ */
+gchar    *tracker_locale_get           (TrackerLocaleID id);
+
+/* Adds a new subscriber to locale change notifications.
+ * Returns a pointer which identifies the subscription.
+ */
+gpointer  tracker_locale_notify_add    (TrackerLocaleID         id,
+                                        TrackerLocaleNotifyFunc func,
+                                        gpointer                user_data,
+                                        GFreeFunc               destroy_notify);
+
+/* Remove a given subscriber, passing the id you got in _add() */
+void      tracker_locale_notify_remove (gpointer notification_id);
+
+
+G_END_DECLS
+
+#endif /* __LIBTRACKER_COMMON_LOCALE_H__ */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]