[gnome-desktop] gnome-xkb-info: Added to parse and make XKB xml descriptions available



commit db4d37b36c3140b1e9d83dbf58cc98fc6e6aa4ad
Author: Rui Matos <tiagomatos gmail com>
Date:   Mon May 21 17:25:47 2012 +0200

    gnome-xkb-info: Added to parse and make XKB xml descriptions available
    
    xkeyboard-config's xml descriptions contain useful information about
    XKB layouts. This class makes it easily available to several core
    components in the desktop.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=676583

 configure.ac                      |   13 +-
 libgnome-desktop/Makefile.am      |    5 +-
 libgnome-desktop/gnome-xkb-info.c |  674 +++++++++++++++++++++++++++++++++++++
 libgnome-desktop/gnome-xkb-info.h |   84 +++++
 4 files changed, 774 insertions(+), 2 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index be495d4..d3c1a62 100644
--- a/configure.ac
+++ b/configure.ac
@@ -144,7 +144,18 @@ AC_SUBST(XLIB_LIBS)
 
 dnl pkg-config dependency checks
 
-PKG_CHECK_MODULES(GNOME_DESKTOP, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED gtk+-3.0 >= $GTK_REQUIRED glib-2.0 >= $GLIB_REQUIRED gio-2.0 >= $GLIB_REQUIRED gsettings-desktop-schemas >= $GSETTINGS_DESKTOP_SCHEMAS_REQUIRED xrandr >= $XRANDR_REQUIRED xext >= $XEXT_REQUIRED)
+PKG_CHECK_MODULES(GNOME_DESKTOP, gdk-pixbuf-2.0 >= $GDK_PIXBUF_REQUIRED
+                                 gtk+-3.0 >= $GTK_REQUIRED
+                                 glib-2.0 >= $GLIB_REQUIRED
+                                 gio-2.0 >= $GLIB_REQUIRED
+                                 gsettings-desktop-schemas >= $GSETTINGS_DESKTOP_SCHEMAS_REQUIRED
+                                 xrandr >= $XRANDR_REQUIRED
+                                 xext >= $XEXT_REQUIRED
+                                 xkeyboard-config
+                                 xkbfile)
+
+XKB_BASE=$($PKG_CONFIG --variable xkb_base xkeyboard-config)
+AC_SUBST(XKB_BASE)
 
 AC_CACHE_CHECK(for timerfd_create(2) system call,
     gnome_cv_timerfd,AC_COMPILE_IFELSE([AC_LANG_PROGRAM([
diff --git a/libgnome-desktop/Makefile.am b/libgnome-desktop/Makefile.am
index 800d36c..251f8f6 100644
--- a/libgnome-desktop/Makefile.am
+++ b/libgnome-desktop/Makefile.am
@@ -9,6 +9,7 @@ AM_CPPFLAGS =							\
 	-DG_LOG_DOMAIN=\"GnomeDesktop\"				\
 	-DGNOMELOCALEDIR=\""$(prefix)/$(DATADIRNAME)/locale\""	\
 	-DPNP_IDS=\""$(PNP_IDS)"\"				\
+	-DXKB_BASE=\""$(XKB_BASE)"\"				\
 	$(DISABLE_DEPRECATED_CFLAGS)
 
 AM_CFLAGS = $(WARN_CFLAGS)
@@ -26,6 +27,7 @@ introspection_sources = 		\
 	gnome-rr-labeler.c		\
 	gnome-pnp-ids.c			\
 	gnome-wall-clock.c		\
+	gnome-xkb-info.c		\
 	edid-parse.c
 
 libgnome_desktop_3_la_SOURCES = 	\
@@ -60,7 +62,8 @@ libgnome_desktop_HEADERS = \
         gnome-rr-config.h               \
         gnome-rr-labeler.h		\
         gnome-pnp-ids.h			\
-        gnome-wall-clock.h
+        gnome-wall-clock.h		\
+	gnome-xkb-info.h
 
 if USE_INTERNAL_PNP_IDS
 pnpdatadir = $(datadir)/libgnome-desktop-3.0
diff --git a/libgnome-desktop/gnome-xkb-info.c b/libgnome-desktop/gnome-xkb-info.c
new file mode 100644
index 0000000..7fd73c7
--- /dev/null
+++ b/libgnome-desktop/gnome-xkb-info.c
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBrules.h>
+
+#include <gdk/gdkx.h>
+
+#include <glib/gi18n-lib.h>
+#define XKEYBOARD_CONFIG_(String) ((char *) g_dgettext ("xkeyboard-config", String))
+
+#define GNOME_DESKTOP_USE_UNSTABLE_API
+#include "gnome-xkb-info.h"
+
+#ifndef XKB_RULES_FILE
+#define XKB_RULES_FILE "evdev"
+#endif
+#ifndef XKB_LAYOUT
+#define XKB_LAYOUT "us"
+#endif
+#ifndef XKB_MODEL
+#define XKB_MODEL "pc105+inet"
+#endif
+
+typedef struct _Layout Layout;
+struct _Layout
+{
+  gchar *id;
+  gchar *xkb_name;
+  gchar *short_desc;
+  gchar *description;
+  gboolean is_variant;
+  const Layout *main_layout;
+};
+
+struct _GnomeXkbInfoPrivate
+{
+  GHashTable *layouts_by_short_desc;
+  GHashTable *layouts_by_iso639;
+  GHashTable *layouts_table;
+
+  /* Only used while parsing */
+  Layout *current_parser_layout;
+  Layout *current_parser_variant;
+  gchar  *current_parser_iso639Id;
+  gchar **current_parser_text;
+};
+
+G_DEFINE_TYPE (GnomeXkbInfo, gnome_xkb_info, G_TYPE_OBJECT);
+
+static void
+free_layout (gpointer data)
+{
+  Layout *layout = data;
+
+  g_return_if_fail (layout != NULL);
+
+  g_free (layout->id);
+  g_free (layout->xkb_name);
+  g_free (layout->short_desc);
+  g_free (layout->description);
+  g_free (layout);
+}
+
+/**
+ * gnome_xkb_info_get_var_defs: (skip)
+ * @rules: (out) (transfer full): location to store the rules file
+ * path. Use g_free() when it's no longer needed
+ * @var_defs: (out) (transfer full): location to store a
+ * #XkbRF_VarDefsRec pointer. Use gnome_xkb_info_free_var_defs() to
+ * free it
+ *
+ * Gets both the XKB rules file path and the current XKB parameters in
+ * use by the X server.
+ *
+ * Since: 3.6
+ */
+void
+gnome_xkb_info_get_var_defs (gchar            **rules,
+                             XkbRF_VarDefsRec **var_defs)
+{
+  Display *display = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ());
+  char *tmp;
+
+  g_return_if_fail (rules != NULL);
+  g_return_if_fail (var_defs != NULL);
+
+  *rules = NULL;
+  *var_defs = g_new0 (XkbRF_VarDefsRec, 1);
+
+  gdk_error_trap_push ();
+
+  /* Get it from the X property or fallback on defaults */
+  if (!XkbRF_GetNamesProp (display, rules, *var_defs) || !*rules)
+    {
+      *rules = strdup (XKB_RULES_FILE);
+      (*var_defs)->model = strdup (XKB_MODEL);
+      (*var_defs)->layout = strdup (XKB_LAYOUT);
+      (*var_defs)->variant = NULL;
+      (*var_defs)->options = NULL;
+    }
+
+  gdk_error_trap_pop_ignored ();
+
+  tmp = *rules;
+
+  if (*rules[0] == '/')
+    *rules = g_strdup (*rules);
+  else
+    *rules = g_build_filename (XKB_BASE, "rules", *rules, NULL);
+
+  free (tmp);
+}
+
+/**
+ * gnome_xkb_info_free_var_defs: (skip)
+ * @var_defs: #XkbRF_VarDefsRec instance to free
+ *
+ * Frees an #XkbRF_VarDefsRec instance allocated by
+ * gnome_xkb_info_get_var_defs().
+ *
+ * Since: 3.6
+ */
+void
+gnome_xkb_info_free_var_defs (XkbRF_VarDefsRec *var_defs)
+{
+  g_return_if_fail (var_defs != NULL);
+
+  free (var_defs->model);
+  free (var_defs->layout);
+  free (var_defs->variant);
+  free (var_defs->options);
+
+  g_free (var_defs);
+}
+
+static gchar *
+get_xml_rules_file_path (void)
+{
+  XkbRF_VarDefsRec *xkb_var_defs;
+  gchar *rules_file;
+  gchar *xml_rules_file;
+
+  gnome_xkb_info_get_var_defs (&rules_file, &xkb_var_defs);
+  gnome_xkb_info_free_var_defs (xkb_var_defs);
+
+  xml_rules_file = g_strdup_printf ("%s.xml", rules_file);
+  g_free (rules_file);
+
+  return xml_rules_file;
+}
+
+static void
+parse_start_element (GMarkupParseContext  *context,
+                     const gchar          *element_name,
+                     const gchar         **attribute_names,
+                     const gchar         **attribute_values,
+                     gpointer              data,
+                     GError              **error)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  if (priv->current_parser_text)
+    {
+      g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                   "Expected character data but got element '%s'", element_name);
+      return;
+    }
+
+  if (strcmp (element_name, "name") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->xkb_name;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->xkb_name;
+    }
+  else if (strcmp (element_name, "description") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->description;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->description;
+    }
+  else if (strcmp (element_name, "shortDescription") == 0)
+    {
+      if (priv->current_parser_variant)
+        priv->current_parser_text = &priv->current_parser_variant->short_desc;
+      else if (priv->current_parser_layout)
+        priv->current_parser_text = &priv->current_parser_layout->short_desc;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      priv->current_parser_text = &priv->current_parser_iso639Id;
+    }
+  else if (strcmp (element_name, "layout") == 0)
+    {
+      if (priv->current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements can't be nested");
+          return;
+        }
+
+      priv->current_parser_layout = g_new0 (Layout, 1);
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      if (priv->current_parser_variant)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements can't be nested");
+          return;
+        }
+
+      if (!priv->current_parser_layout)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must be inside 'layout' elements");
+          return;
+        }
+
+      priv->current_parser_variant = g_new0 (Layout, 1);
+      priv->current_parser_variant->is_variant = TRUE;
+      priv->current_parser_variant->main_layout = priv->current_parser_layout;
+    }
+}
+
+static void
+maybe_replace (GHashTable *table,
+               gchar      *key,
+               Layout     *new_layout)
+{
+  /* There might be multiple layouts for the same language. In that
+   * case considering the "canonical" layout to be the one with the
+   * shorter description seems to be good enough. */
+  Layout *layout;
+  gboolean exists;
+  gboolean replace = TRUE;
+
+  exists = g_hash_table_lookup_extended (table, key, NULL, (gpointer *)&layout);
+  if (exists)
+    replace = strlen (new_layout->description) < strlen (layout->description);
+  if (replace)
+    g_hash_table_replace (table, key, new_layout);
+}
+
+static void
+parse_end_element (GMarkupParseContext  *context,
+                   const gchar          *element_name,
+                   gpointer              data,
+                   GError              **error)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  if (strcmp (element_name, "layout") == 0)
+    {
+      if (!priv->current_parser_layout->description || !priv->current_parser_layout->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'layout' elements must enclose 'description' and 'name' elements");
+          return;
+        }
+
+      priv->current_parser_layout->id = g_strdup (priv->current_parser_layout->xkb_name);
+
+      if (priv->current_parser_layout->short_desc)
+        maybe_replace (priv->layouts_by_short_desc,
+                       priv->current_parser_layout->short_desc, priv->current_parser_layout);
+
+      g_hash_table_replace (priv->layouts_table,
+                            priv->current_parser_layout->id,
+                            priv->current_parser_layout);
+      priv->current_parser_layout = NULL;
+    }
+  else if (strcmp (element_name, "variant") == 0)
+    {
+      if (!priv->current_parser_variant->description || !priv->current_parser_variant->xkb_name)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'variant' elements must enclose 'description' and 'name' elements");
+          return;
+        }
+
+      priv->current_parser_variant->id = g_strjoin ("+",
+                                                    priv->current_parser_layout->xkb_name,
+                                                    priv->current_parser_variant->xkb_name,
+                                                    NULL);
+
+      if (priv->current_parser_variant->short_desc)
+        maybe_replace (priv->layouts_by_short_desc,
+                       priv->current_parser_variant->short_desc, priv->current_parser_variant);
+
+      g_hash_table_replace (priv->layouts_table,
+                            priv->current_parser_variant->id,
+                            priv->current_parser_variant);
+      priv->current_parser_variant = NULL;
+    }
+  else if (strcmp (element_name, "iso639Id") == 0)
+    {
+      if (!priv->current_parser_iso639Id)
+        {
+          g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT,
+                       "'iso639Id' elements must enclose text");
+          return;
+        }
+
+      if (priv->current_parser_layout)
+        maybe_replace (priv->layouts_by_iso639,
+                       priv->current_parser_iso639Id, priv->current_parser_layout);
+      else if (priv->current_parser_variant)
+        maybe_replace (priv->layouts_by_iso639,
+                       priv->current_parser_iso639Id, priv->current_parser_variant);
+      else
+        g_free (priv->current_parser_iso639Id);
+
+      priv->current_parser_iso639Id = NULL;
+    }
+}
+
+static void
+parse_text (GMarkupParseContext  *context,
+            const gchar          *text,
+            gsize                 text_len,
+            gpointer              data,
+            GError              **error)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  if (priv->current_parser_text)
+    {
+      *priv->current_parser_text = g_strndup (text, text_len);
+      priv->current_parser_text = NULL;
+    }
+}
+
+static void
+parse_error (GMarkupParseContext *context,
+             GError              *error,
+             gpointer             data)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (data)->priv;
+
+  free_layout (priv->current_parser_layout);
+  free_layout (priv->current_parser_variant);
+  g_free (priv->current_parser_iso639Id);
+}
+
+static const GMarkupParser markup_parser = {
+  parse_start_element,
+  parse_end_element,
+  parse_text,
+  NULL,
+  parse_error
+};
+
+static void
+parse_rules_file (GnomeXkbInfo *self)
+{
+  GnomeXkbInfoPrivate *priv = self->priv;
+  gchar *buffer;
+  gsize length;
+  GMarkupParseContext *context;
+  GError *error = NULL;
+  gchar *file_path = get_xml_rules_file_path ();
+
+  g_file_get_contents (file_path, &buffer, &length, &error);
+  g_free (file_path);
+  if (error)
+    {
+      g_warning ("Failed to read XKB rules file: %s", error->message);
+      g_error_free (error);
+      return;
+    }
+
+  priv->layouts_by_short_desc = g_hash_table_new (g_str_hash, g_str_equal);
+  priv->layouts_by_iso639 = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+  /* This is the "master" table so it assumes memory "ownership". */
+  priv->layouts_table = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_layout);
+
+  context = g_markup_parse_context_new (&markup_parser, 0, self, NULL);
+  g_markup_parse_context_parse (context, buffer, length, &error);
+  g_markup_parse_context_free (context);
+  g_free (buffer);
+  if (error)
+    {
+      g_warning ("Failed to parse XKB rules file: %s", error->message);
+      g_error_free (error);
+      g_hash_table_destroy (priv->layouts_by_short_desc);
+      priv->layouts_by_short_desc = NULL;
+      g_hash_table_destroy (priv->layouts_by_iso639);
+      priv->layouts_by_iso639 = NULL;
+      g_hash_table_destroy (priv->layouts_table);
+      priv->layouts_table = NULL;
+    }
+}
+
+static gboolean
+ensure_rules_are_parsed (GnomeXkbInfo *self)
+{
+  GnomeXkbInfoPrivate *priv = self->priv;
+
+  if (!priv->layouts_table)
+    parse_rules_file (self);
+
+  return !!priv->layouts_table;
+}
+
+static void
+gnome_xkb_info_init (GnomeXkbInfo *self)
+{
+  self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, GNOME_TYPE_XKB_INFO, GnomeXkbInfoPrivate);
+}
+
+static void
+gnome_xkb_info_finalize (GObject *self)
+{
+  GnomeXkbInfoPrivate *priv = GNOME_XKB_INFO (self)->priv;
+
+  if (priv->layouts_by_short_desc)
+    g_hash_table_destroy (priv->layouts_by_short_desc);
+  if (priv->layouts_by_iso639)
+    g_hash_table_destroy (priv->layouts_by_iso639);
+  if (priv->layouts_table)
+    g_hash_table_destroy (priv->layouts_table);
+
+  G_OBJECT_CLASS (gnome_xkb_info_parent_class)->finalize (self);
+}
+
+static void
+gnome_xkb_info_class_init (GnomeXkbInfoClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize = gnome_xkb_info_finalize;
+
+  g_type_class_add_private (gobject_class, sizeof (GnomeXkbInfoPrivate));
+}
+
+/**
+ * gnome_xkb_info_new:
+ *
+ * Returns: (transfer full): a new #GnomeXkbInfo instance.
+ */
+GnomeXkbInfo *
+gnome_xkb_info_new (void)
+{
+  return g_object_new (GNOME_TYPE_XKB_INFO, NULL);
+}
+
+/**
+ * gnome_xkb_info_get_all_layouts:
+ * @self: a #GnomeXkbInfo
+ *
+ * Returns a list of all layout identifiers we know about.
+ *
+ * Return value: (transfer container) (element-type gchar): the list
+ * of layout names. The caller takes ownership of the #GList but not
+ * of the strings themselves, those are internally allocated and must
+ * not be modified.
+ *
+ * Since: 3.6
+ */
+GList *
+gnome_xkb_info_get_all_layouts (GnomeXkbInfo *self)
+{
+  GnomeXkbInfoPrivate *priv;
+
+  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), NULL);
+
+  priv = self->priv;
+
+  if (!ensure_rules_are_parsed (self))
+    return NULL;
+
+  return g_hash_table_get_keys (priv->layouts_table);
+}
+
+/**
+ * gnome_xkb_info_get_layout_info:
+ * @self: a #GnomeXkbInfo
+ * @id: layout's identifier about which to retrieve the info
+ * @display_name: (out) (allow-none) (transfer none): location to store
+ * the layout's display name, or %NULL
+ * @short_name: (out) (allow-none) (transfer none): location to store
+ * the layout's short name, or %NULL
+ * @xkb_layout: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB name, or %NULL
+ * @xkb_variant: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB variant, or %NULL
+ *
+ * Retrieves information about a layout. Both @display_name and
+ * @short_name are suitable to show in UIs and might be localized if
+ * translations are available.
+ *
+ * Some layouts don't provide a short name (2 or 3 letters) or don't
+ * specify a XKB variant, in those cases @short_name or @xkb_variant
+ * are empty strings, i.e. "".
+ *
+ * If the given layout doesn't exist the return value is %FALSE and
+ * all the (out) parameters are set to %NULL.
+ *
+ * Return value: %TRUE if the layout exists or %FALSE otherwise.
+ *
+ * Since: 3.6
+ */
+gboolean
+gnome_xkb_info_get_layout_info (GnomeXkbInfo *self,
+                                const gchar  *id,
+                                const gchar **display_name,
+                                const gchar **short_name,
+                                const gchar **xkb_layout,
+                                const gchar **xkb_variant)
+{
+  GnomeXkbInfoPrivate *priv;
+  const Layout *layout;
+
+  if (display_name)
+    *display_name = NULL;
+  if (short_name)
+    *short_name = NULL;
+  if (xkb_layout)
+    *xkb_layout = NULL;
+  if (xkb_variant)
+    *xkb_variant = NULL;
+
+  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), FALSE);
+
+  priv = self->priv;
+
+  if (!ensure_rules_are_parsed (self))
+    return FALSE;
+
+  if (!g_hash_table_lookup_extended (priv->layouts_table, id, NULL, (gpointer *)&layout))
+    return FALSE;
+
+  if (display_name)
+    *display_name = XKEYBOARD_CONFIG_(layout->description);
+
+  if (!layout->is_variant)
+    {
+      if (short_name)
+        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : "");
+      if (xkb_layout)
+        *xkb_layout = layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = "";
+    }
+  else
+    {
+      if (short_name)
+        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc :
+                        layout->main_layout->short_desc ? layout->main_layout->short_desc : "");
+      if (xkb_layout)
+        *xkb_layout = layout->main_layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = layout->xkb_name;
+    }
+
+  return TRUE;
+}
+
+/**
+ * gnome_xkb_info_get_layout_info_for_language:
+ * @self: a #GnomeXkbInfo
+ * @language: an ISO 639 code
+ * @id: (out) (allow-none) (transfer none): location to store the
+ * layout's indentifier, or %NULL
+ * @display_name: (out) (allow-none) (transfer none): location to store
+ * the layout's display name, or %NULL
+ * @short_name: (out) (allow-none) (transfer none): location to store
+ * the layout's short name, or %NULL
+ * @xkb_layout: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB name, or %NULL
+ * @xkb_variant: (out) (allow-none) (transfer none): location to store
+ * the layout's XKB variant, or %NULL
+ *
+ * Retrieves the layout that better fits @language. It also fetches
+ * information about that layout like gnome_xkb_info_get_layout_info().
+ *
+ * If a layout can't be found the return value is %FALSE and all the
+ * (out) parameters are set to %NULL.
+ *
+ * Return value: %TRUE if a layout exists or %FALSE otherwise.
+ *
+ * Since: 3.6
+ */
+gboolean
+gnome_xkb_info_get_layout_info_for_language (GnomeXkbInfo *self,
+                                             const gchar  *language,
+                                             const gchar **id,
+                                             const gchar **display_name,
+                                             const gchar **short_name,
+                                             const gchar **xkb_layout,
+                                             const gchar **xkb_variant)
+{
+  GnomeXkbInfoPrivate *priv;
+  const Layout *layout;
+
+  if (id)
+    *id = NULL;
+  if (display_name)
+    *display_name = NULL;
+  if (short_name)
+    *short_name = NULL;
+  if (xkb_layout)
+    *xkb_layout = NULL;
+  if (xkb_variant)
+    *xkb_variant = NULL;
+
+  g_return_val_if_fail (GNOME_IS_XKB_INFO (self), FALSE);
+
+  priv = self->priv;
+
+  if (!ensure_rules_are_parsed (self))
+    return FALSE;
+
+  /* First look in the proper language codes index, if we can't find
+   * it there try again on the (untranslated) short descriptions since
+   * sometimes those will give us a good match. */
+  if (!g_hash_table_lookup_extended (priv->layouts_by_iso639, language, NULL, (gpointer *)&layout))
+    if (!g_hash_table_lookup_extended (priv->layouts_by_short_desc, language, NULL, (gpointer *)&layout))
+      return FALSE;
+
+  if (id)
+    *id = layout->id;
+  if (display_name)
+    *display_name = XKEYBOARD_CONFIG_(layout->description);
+
+  if (!layout->is_variant)
+    {
+      if (short_name)
+        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc : "");
+      if (xkb_layout)
+        *xkb_layout = layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = "";
+    }
+  else
+    {
+      if (short_name)
+        *short_name = XKEYBOARD_CONFIG_(layout->short_desc ? layout->short_desc :
+                        layout->main_layout->short_desc ? layout->main_layout->short_desc : "");
+      if (xkb_layout)
+        *xkb_layout = layout->main_layout->xkb_name;
+      if (xkb_variant)
+        *xkb_variant = layout->xkb_name;
+    }
+
+  return TRUE;
+}
diff --git a/libgnome-desktop/gnome-xkb-info.h b/libgnome-desktop/gnome-xkb-info.h
new file mode 100644
index 0000000..38af9df
--- /dev/null
+++ b/libgnome-desktop/gnome-xkb-info.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2012 Red Hat, Inc.
+ *
+ * Written by: Rui Matos <rmatos redhat com>
+ *
+ * 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 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#ifndef __GNOME_XKB_INFO_H__
+#define __GNOME_XKB_INFO_H__
+
+#ifndef GNOME_DESKTOP_USE_UNSTABLE_API
+#error    This is unstable API. You must define GNOME_DESKTOP_USE_UNSTABLE_API before including gnome-xkb-info.h
+#endif
+
+#include <stdio.h>
+
+#include <glib-object.h>
+
+#include <X11/XKBlib.h>
+#include <X11/extensions/XKBrules.h>
+
+G_BEGIN_DECLS
+
+#define GNOME_TYPE_XKB_INFO            (gnome_xkb_info_get_type ())
+#define GNOME_XKB_INFO(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GNOME_TYPE_XKB_INFO, GnomeXkbInfo))
+#define GNOME_XKB_INFO_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass),  GNOME_TYPE_XKB_INFO, GnomeXkbInfoClass))
+#define GNOME_IS_XKB_INFO(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GNOME_TYPE_XKB_INFO))
+#define GNOME_IS_XKB_INFO_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass),  GNOME_TYPE_XKB_INFO))
+#define GNOME_XKB_INFO_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj),  GNOME_TYPE_XKB_INFO, GnomeXkbInfoClass))
+
+typedef struct _GnomeXkbInfoPrivate GnomeXkbInfoPrivate;
+typedef struct _GnomeXkbInfo GnomeXkbInfo;
+typedef struct _GnomeXkbInfoClass GnomeXkbInfoClass;
+
+struct _GnomeXkbInfo
+{
+  GObject parent_object;
+
+  GnomeXkbInfoPrivate *priv;
+};
+
+struct _GnomeXkbInfoClass
+{
+  GObjectClass parent_class;
+};
+
+GType           gnome_xkb_info_get_type                         (void);
+GnomeXkbInfo   *gnome_xkb_info_new                              (void);
+GList          *gnome_xkb_info_get_all_layouts                  (GnomeXkbInfo *self);
+gboolean        gnome_xkb_info_get_layout_info                  (GnomeXkbInfo *self,
+                                                                 const gchar  *id,
+                                                                 const gchar **display_name,
+                                                                 const gchar **short_name,
+                                                                 const gchar **xkb_layout,
+                                                                 const gchar **xkb_variant);
+gboolean        gnome_xkb_info_get_layout_info_for_language     (GnomeXkbInfo *self,
+                                                                 const gchar  *language,
+                                                                 const gchar **id,
+                                                                 const gchar **display_name,
+                                                                 const gchar **short_name,
+                                                                 const gchar **xkb_layout,
+                                                                 const gchar **xkb_variant);
+
+void            gnome_xkb_info_get_var_defs                     (gchar            **rules,
+                                                                 XkbRF_VarDefsRec **var_defs);
+void            gnome_xkb_info_free_var_defs                    (XkbRF_VarDefsRec  *var_defs);
+
+G_END_DECLS
+
+#endif  /* __GNOME_XKB_INFO_H__ */



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