[gnome-characters/wip/dueno/local-scripts] categoryList: Deduce languages from input-sources



commit 235720c9d3d06fa8732da19bb41d90224da1a4a0
Author: Daiki Ueno <dueno src gnome org>
Date:   Tue Jun 16 17:56:55 2015 +0900

    categoryList: Deduce languages from input-sources
    
    It first reads the input-source settings and deduce the language list.
    Then, for each language, deduce the script list from translations.
    
    This requires a couple of API additions to gnome-desktop:
    
      - gnome_xkb_info_get_languages_for_layout, which gets language codes
        associated with an XKB layout
    
      - gnome_normalize_language_code, which canonicalizes a 3-letter
        language code to a 2-letter code, so we can build a valid locale
        name
    
    https://bugzilla.gnome.org/show_bug.cgi?id=743643

 data/org.gnome.Characters.gschema.xml |    8 ++--
 lib/gc.c                              |   90 +++++++++++++++++++++++++++++++
 lib/gc.h                              |    6 ++
 src/categoryList.js                   |   95 ++++++++++++++++++++++++++-------
 src/characterList.js                  |    5 ++
 src/window.js                         |   18 +++---
 6 files changed, 190 insertions(+), 32 deletions(-)
---
diff --git a/data/org.gnome.Characters.gschema.xml b/data/org.gnome.Characters.gschema.xml
index 09ae962..8053862 100644
--- a/data/org.gnome.Characters.gschema.xml
+++ b/data/org.gnome.Characters.gschema.xml
@@ -14,16 +14,16 @@
       <default>100</default>
       <summary>Maximum recent characters</summary>
     </key>
-    <key name="scripts" type="as">
+    <key name="local-scripts" type="as">
       <!-- TRANSLATORS: list Unicode scripts used in your language.
           For Latin languages, keep it empty.  -->
-      <default l10n="messages" context="scripts">[]</default>
+      <default l10n="messages" context="local-scripts">[]</default>
       <summary>A list of Unicode scripts used in this locale.</summary>
     </key>
-    <key name="scripts-character" type="s">
+    <key name="local-scripts-character" type="s">
       <!-- TRANSLATORS: put the most representative character in your language.
           This will be used as a label of the 'Local scripts' tab.  -->
-      <default l10n="messages" context="scripts-character">'?'</default>
+      <default l10n="messages" context="local-scripts-character">'?'</default>
       <summary>Character representing Unicode scripts used in this locale.</summary>
     </key>
   </schema>
diff --git a/lib/gc.c b/lib/gc.c
index a1efec5..8ea432a 100644
--- a/lib/gc.c
+++ b/lib/gc.c
@@ -2,6 +2,8 @@
 
 #include "gc.h"
 
+#include <langinfo.h>
+#include <locale.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unicase.h>
@@ -917,3 +919,91 @@ gc_pango_context_font_has_glyph (PangoContext *context,
 
   return retval == 0;
 }
+
+/**
+ * gc_get_current_language:
+ *
+ * Returns: (transfer full): an ISO639 two-letter language code
+ */
+gchar *
+gc_get_current_language (void)
+{
+  const gchar *locale = setlocale (LC_MESSAGES, NULL);
+  size_t length;
+
+  if (!locale || !*locale)
+    return NULL;
+
+  length = strcspn (locale, "_.@");
+
+  return g_strndup (locale, length);
+}
+
+/**
+ * gc_get_scripts_for_locale:
+ * @locale: a locale name
+ *
+ * Returns: (transfer full) (element-type utf8): a list of script names.
+ */
+GList *
+gc_get_scripts_for_locale (const gchar *locale)
+{
+  gchar *old_locale;
+  GSettings *settings;
+  GVariant *value;
+  GList *result = NULL;
+
+  old_locale = g_strdup (setlocale (LC_MESSAGES, locale));
+  if (!old_locale)
+    return NULL;
+
+  settings = g_settings_new ("org.gnome.Characters");
+  value = g_settings_get_default_value (settings, "local-scripts");
+  if (value)
+    {
+      GVariantIter iter;
+      gchar *script;
+
+      g_variant_iter_init (&iter, value);
+      while (g_variant_iter_next (&iter, "s", &script))
+       result = g_list_append (result, script);
+      g_variant_unref (value);
+    }
+
+  setlocale (LC_MESSAGES, old_locale);
+  g_free (old_locale);
+
+  return result;
+}
+
+/**
+ * gc_get_character_for_locale:
+ * @locale: a locale name
+ *
+ * Returns: (nullable) (transfer full): a string
+ */
+gchar *
+gc_get_character_for_locale (const gchar *locale)
+{
+  gchar *old_locale;
+  GSettings *settings;
+  GVariant *value;
+  gchar *result = NULL;
+
+  old_locale = g_strdup (setlocale (LC_MESSAGES, locale));
+  if (!old_locale)
+    return g_strdup ("?");
+
+  settings = g_settings_new ("org.gnome.Characters");
+  value = g_settings_get_default_value (settings, "local-scripts-character");
+  setlocale (LC_MESSAGES, old_locale);
+  g_free (old_locale);
+
+  if (value)
+    {
+      result = g_variant_dup_string (value, NULL);
+      g_variant_unref (value);
+    }
+
+  return result;
+}
diff --git a/lib/gc.h b/lib/gc.h
index 6383cdd..53c4baa 100644
--- a/lib/gc.h
+++ b/lib/gc.h
@@ -78,6 +78,12 @@ gboolean        gc_pango_context_font_has_glyph
                                           PangoFont            *font,
                                           gunichar              uc);
 
+gchar          *gc_get_current_language   (void);
+GList          *gc_get_scripts_for_locale
+                                          (const gchar          *locale);
+gchar          *gc_get_character_for_locale
+                                          (const gchar          *locale);
+
 G_END_DECLS
 
 #endif /* __GC_H__ */
diff --git a/src/categoryList.js b/src/categoryList.js
index c1f90cc..d87e506 100644
--- a/src/categoryList.js
+++ b/src/categoryList.js
@@ -21,14 +21,16 @@ const Params = imports.params;
 const Cairo = imports.cairo;
 const PangoCairo = imports.gi.PangoCairo;
 const Pango = imports.gi.Pango;
+const GnomeDesktop = imports.gi.GnomeDesktop;
 const Gio = imports.gi.Gio;
 const GLib = imports.gi.GLib;
 const Gtk = imports.gi.Gtk;
 const Gettext = imports.gettext;
 const Gc = imports.gi.Gc;
 const Main = imports.main;
+const Util = imports.util;
 
-const Category = [
+const BaseCategoryList = [
     {
         name: 'recent',
         category: Gc.Category.NONE,
@@ -82,11 +84,6 @@ const Category = [
         category: Gc.Category.EMOTICON,
         title: N_('Emoticons'),
         icon_name: 'face-smile-symbolic'
-    },
-    {
-        name: 'local',
-        category: Gc.Category.NONE,
-        title: N_('Local scripts')
     }
 ];
 
@@ -107,8 +104,8 @@ const CategoryListRowWidget = new Lang.Class({
         this.add(hbox);
 
         let image;
-        if (category.name == 'local') {
-            image = this._createScriptsImage();
+        if ('scripts' in category) {
+            image = this._createCharacterImage(category.character);
         } else {
             let icon = new Gio.ThemedIcon({ name: category.icon_name });
             image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.LARGE_TOOLBAR);
@@ -122,7 +119,7 @@ const CategoryListRowWidget = new Lang.Class({
         hbox.pack_start(label, true, true, 0);
     },
 
-    _createScriptsImage: function() {
+    _createCharacterImage: function(uc) {
         let surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 24, 24);
         let cr = new CairoContext(surface);
         let layout = PangoCairo.create_layout(cr);
@@ -133,15 +130,79 @@ const CategoryListRowWidget = new Lang.Class({
         fontDescription.set_size(16 * Pango.SCALE);
         layout.set_font_description(fontDescription);
 
-        let [uc, length] =
-            Main.settings.get_value('scripts-character').get_string();
-        layout.set_text(uc, length);
+        layout.set_text(uc, -1);
         PangoCairo.show_layout(cr, layout);
 
         return Gtk.Image.new_from_surface(surface);
     }
 });
 
+let _categoryList = null;
+
+function getCategoryList() {
+    if (_categoryList == null) {
+        _categoryList = BaseCategoryList.slice();
+
+        // Deduce language list from input-source settings.
+        let allLanguages = {};
+        let settings = Util.getSettings('org.gnome.desktop.input-sources',
+                                        '/org/gnome/desktop/input-sources/');
+        let xkbInfo = new GnomeDesktop.XkbInfo();
+        if (settings && xkbInfo.get_languages_for_layout) {
+            let sources = settings.get_value('sources').deep_unpack();
+            for (let i in sources) {
+                let [type, id] = sources[i];
+                if (type != 'xkb')
+                    continue;
+
+                let languages = xkbInfo.get_languages_for_layout(id);
+                for (let j in languages) {
+                    let language_code =
+                        GnomeDesktop.normalize_language_code(languages[j]);
+                    if (!(languages in allLanguages)) {
+                        allLanguages[language_code] =
+                            GnomeDesktop.get_language_from_code(language_code,
+                                                                null);
+                    }
+                }
+            }
+        }
+
+        // Add current locale language to allLangs.
+        let language_code = Gc.get_current_language();
+        if (!(language_code in allLanguages)) {
+            allLanguages[language_code] =
+                GnomeDesktop.get_language_from_code(language_code, null);
+        }
+
+        let allLocales = GnomeDesktop.get_all_locales();
+        // Loop over allLangs and deduce Unicode scripts used in each language.
+        for (let language_code in allLanguages) {
+            let locales = allLocales.filter(function (value) {
+                return value == language_code ||
+                    value.startsWith(language_code + '_');
+            });
+            for (let index in locales) {
+                let locale = locales[index];
+                let scripts = Gc.get_scripts_for_locale(locale);
+                let character = Gc.get_character_for_locale(locale);
+                if (scripts.length > 0) {
+                    let category = {
+                        name: language_code,
+                        category: Gc.Category.NONE,
+                        title: allLanguages[language_code],
+                        scripts: scripts,
+                        character: character
+                    }
+                    _categoryList.push(category);
+                    break;
+                }
+            }
+        }
+    }
+    return _categoryList;
+}
+
 const CategoryListWidget = new Lang.Class({
     Name: 'CategoryListWidget',
     Extends: Gtk.ListBox,
@@ -153,13 +214,9 @@ const CategoryListWidget = new Lang.Class({
         // Mimic GtkStackSidebar to take advantage of the standard theme.
         this.get_style_context().add_class('sidebar');
 
-        let scripts = Main.settings.get_value('scripts').get_strv();
-        let scripts_visible = scripts.length > 0;
-
-        for (let index in Category) {
-            let category = Category[index];
-            if (category.name == 'local' && !scripts_visible)
-                continue;
+        let categories = getCategoryList();
+        for (let index in categories) {
+            let category = categories[index];
             let rowWidget = new CategoryListRowWidget({}, category);
             // Mimic GtkStackSidebar to take advantage of the standard theme.
             rowWidget.get_style_context().add_class('sidebar-item');
diff --git a/src/characterList.js b/src/characterList.js
index 4d3ec08..2f79f43 100644
--- a/src/characterList.js
+++ b/src/characterList.js
@@ -344,6 +344,11 @@ const CharacterListView = new Lang.Class({
     },
 
     searchByCategory: function(category) {
+        if ('scripts' in category) {
+            this.searchByScripts(category.scripts);
+            return;
+        }
+
         this._startSearch()
         Gc.search_by_category(
             category.category,
diff --git a/src/window.js b/src/window.js
index 59f39e4..aaaf681 100644
--- a/src/window.js
+++ b/src/window.js
@@ -199,8 +199,9 @@ const MainWindow = new Lang.Class({
         // FIXME: we could use Gtk.Container.get_child to obtain the
         // title, but it is not introspectable.
         let category = null;
-        for (let index in CategoryList.Category) {
-            category = CategoryList.Category[index];
+        let categories = CategoryList.getCategoryList();
+        for (let index in categories) {
+            category = categories[index];
             if (category.name == name)
                 break;
         }
@@ -270,8 +271,9 @@ const MainView = new Lang.Class({
         this._characterLists = {};
 
         let characterList;
-        for (let index in CategoryList.Category) {
-            let category = CategoryList.Category[index];
+        let categories = CategoryList.getCategoryList();
+        for (let index in categories) {
+            let category = categories[index];
             characterList = this._createCharacterList(
                 category.name, _('%s Character List').format(category.title));
             // FIXME: Can't use GtkContainer.child_get_property.
@@ -330,13 +332,11 @@ const MainView = new Lang.Class({
                 characterList.setCharacters(this.recentCharacters);
                 characterList.updateCharacterList();
             }
-        } else if (name == 'local') {
-            let scripts = Main.settings.get_value('scripts').get_strv();
-            characterList.searchByScripts(scripts);
         } else {
             let category = null;
-            for (let index in CategoryList.Category) {
-                category = CategoryList.Category[index];
+            let categories = CategoryList.getCategoryList();
+            for (let index in categories) {
+                category = categories[index];
                 if (category.name == name)
                     break;
             }


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