[gnome-characters] categoryList: Support non-Latin scripts



commit 5e086c9ed36a5d23bbeb4390422b0eeab4634f00
Author: Daiki Ueno <dueno src gnome org>
Date:   Wed Jul 1 10:57:01 2015 +0900

    categoryList: Support non-Latin scripts
    
    This patch renames the "Latin" page to "Letters" and add user's
    local characters into that page.  The local characters are deduced from
    input-source settings.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=743643

 src/categoryList.js  |  154 ++++++++++++++++++++++++++++++++++++++++++++++++--
 src/characterList.js |   21 +++++++
 src/window.js        |   37 ++++---------
 3 files changed, 181 insertions(+), 31 deletions(-)
---
diff --git a/src/categoryList.js b/src/categoryList.js
index 82beff7..3ed789e 100644
--- a/src/categoryList.js
+++ b/src/categoryList.js
@@ -18,13 +18,15 @@
 
 const Lang = imports.lang;
 const Params = imports.params;
+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 Util = imports.util;
 
-const Category = [
+const BaseCategoryList = [
     {
         name: 'recent',
         category: Gc.Category.NONE,
@@ -68,9 +70,9 @@ const Category = [
         icon_name: 'characters-math-symbolic'
     },
     {
-        name: 'latin',
+        name: 'letters',
         category: Gc.Category.LATIN,
-        title: N_('Latin'),
+        title: N_('Letters'),
         icon_name: 'characters-latin-symbolic'
     },
     {
@@ -120,8 +122,10 @@ const CategoryListWidget = new Lang.Class({
         // Mimic GtkStackSidebar to take advantage of the standard theme.
         this.get_style_context().add_class('sidebar');
 
-        for (let index in Category) {
-            let category = Category[index];
+        this._ensureCategoryList();
+
+        for (let index in this._categoryList) {
+            let category = this._categoryList[index];
             let rowWidget = new CategoryListRowWidget({}, category);
             // Mimic GtkStackSidebar to take advantage of the standard theme.
             rowWidget.get_style_context().add_class('sidebar-item');
@@ -136,4 +140,144 @@ const CategoryListWidget = new Lang.Class({
             category.activate(new GLib.Variant('s', row.category.name));
         }
     },
+
+    _finishListEngines: function(sources, bus, res) {
+        try {
+            let engines = bus.list_engines_async_finish(res);
+            if (engines) {
+                for (let j in engines) {
+                    let engine = engines[j];
+                    let language = engine.get_language();
+                    if (language != null)
+                        this._ibusLanguageList[engine.get_name()] = language;
+                }
+            }
+        } catch (e) {
+            log("Failed to list engines: " + e);
+        }
+        this._finishBuiltScriptList(sources);
+    },
+
+    _ensureIBusLanguageList: function(sources) {
+        if (this._ibusLanguageList != null)
+            return;
+
+        this._ibusLanguageList = {};
+
+        // Don't assume IBus is always available.
+        let ibus;
+        try {
+            ibus = imports.gi.IBus;
+        } catch (e) {
+            this._finishBuiltScriptList(sources);
+            return;
+        }
+
+        ibus.init();
+        let bus = new ibus.Bus();
+        bus.list_engines_async(-1,
+                               null,
+                               Lang.bind(this, function (bus, res) {
+                                   this._finishListEngines(sources, bus, res);
+                               }));
+    },
+
+    _finishBuiltScriptList: function(sources) {
+        let xkbInfo = new GnomeDesktop.XkbInfo();
+        let languages = [];
+        for (let i in sources) {
+            let [type, id] = sources[i];
+            switch (type) {
+            case 'xkb':
+                // FIXME: Remove this check once gnome-desktop gets the
+                // support for that.
+                if (xkbInfo.get_languages_for_layout) {
+                    languages = languages.concat(
+                        xkbInfo.get_languages_for_layout(id));
+                }
+                break;
+            case 'ibus':
+                if (id in this._ibusLanguageList)
+                    languages.push(this._ibusLanguageList[id]);
+                break;
+            }
+        }
+
+        // Add current locale language to allLangs.
+        languages.push(Gc.get_current_language());
+
+        let allScripts = [];
+        for (let i in languages) {
+            let language = GnomeDesktop.normalize_locale(languages[i]);
+            if (language == null)
+                continue;
+            let scripts = Gc.get_scripts_for_language(languages[i]);
+            for (let j in scripts) {
+                let script = scripts[j];
+                // Latin is always added at top, and Han contains too
+                // many characters.
+                if (['Latin', 'Han'].indexOf(script) >= 0)
+                    continue;
+                if (allScripts.indexOf(script) >= 0)
+                    continue;
+                allScripts.push(script);
+            }
+        }
+
+        allScripts.unshift('Latin');
+        let category = this.getCategory('letters');
+        category.scripts = allScripts;
+    },
+
+    _buildScriptList: function() {
+        let settings =
+            Util.getSettings('org.gnome.desktop.input-sources',
+                             '/org/gnome/desktop/input-sources/');
+        if (settings) {
+            let sources = settings.get_value('sources').deep_unpack();
+            let hasIBus = sources.some(function(current, index, array) {
+                return current[0] == 'ibus';
+            });
+            if (hasIBus)
+                this._ensureIBusLanguageList(sources);
+            else
+                this._finishBuiltScriptList(sources);
+        }
+    },
+
+    _ensureCategoryList: function() {
+        if (this._categoryList != null)
+            return;
+
+        this._categoryList = BaseCategoryList.slice();
+
+        // Populate the "scripts" element of the "Letter" category,
+        // based on the input-sources settings.
+        //
+        // This works asynchronously, in the following call flow:
+        //
+        // _buildScriptList()
+        //    if an IBus input-source is configured:
+        //       _ensureIBusLanguageList()
+        //          ibus_bus_list_engines_async()
+        //             _finishListEngines()
+        //                _finishBuiltScriptList()
+        //    else:
+        //       _finishBuiltScriptList()
+        //
+        this._buildScriptList();
+    },
+
+    getCategoryList: function() {
+        return this._categoryList;
+    },
+
+    getCategory: function(name) {
+        for (let index in this._categoryList) {
+            let category = this._categoryList[index];
+            if (category.name == name)
+                return category;
+        }
+        return null;
+    }
 });
diff --git a/src/characterList.js b/src/characterList.js
index 4dbdf59..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,
@@ -376,6 +381,22 @@ const CharacterListView = new Lang.Class({
             }));
     },
 
+    searchByScripts: function(scripts) {
+        this._startSearch()
+        Gc.search_by_scripts(
+            scripts,
+            -1,
+            this._cancellable,
+            Lang.bind(this, function(source_object, res, user_data) {
+                try {
+                    let result = Gc.search_finish(res);
+                    this._finishSearch(result);
+                } catch (e) {
+                    log("Failed to search by scripts: " + e);
+                }
+            }));
+    },
+
     cancelSearch: function() {
         this._cancellable.cancel();
         this._finishSearch([]);
diff --git a/src/window.js b/src/window.js
index 2ff8a73..4d4b8a9 100644
--- a/src/window.js
+++ b/src/window.js
@@ -104,7 +104,7 @@ const MainWindow = new Lang.Class({
         scroll.add(this._categoryList);
         this._sidebar_grid.add(scroll);
 
-        this._mainView = new MainView();
+        this._mainView = new MainView({ categoryList: this._categoryList });
 
         if (this._mainView.recentCharacters.length == 0) {
             let row = this._categoryList.get_row_at_index(1);
@@ -196,17 +196,10 @@ const MainWindow = new Lang.Class({
 
         let [name, length] = v.get_string()
 
-        // 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];
-            if (category.name == name)
-                break;
-        }
+        let category = this._categoryList.getCategory(name);
 
         Util.assertNotEqual(category, null);
-        this._mainView.setPage(category.name);
+        this._mainView.setPage(category);
         this._updateTitle(category.title);
     },
 
@@ -260,6 +253,7 @@ const MainView = new Lang.Class({
     },
 
     _init: function(params) {
+        let filtered = Params.filter(params, { categoryList: null });
         params = Params.fill(params, {
             hexpand: true, vexpand: true,
             transition_type: Gtk.StackTransitionType.CROSSFADE
@@ -268,10 +262,12 @@ const MainView = new Lang.Class({
 
         this._filterFontFamily = null;
         this._characterLists = {};
+        this._categoryList = filtered.categoryList;
 
         let characterList;
-        for (let index in CategoryList.Category) {
-            let category = CategoryList.Category[index];
+        let categories = this._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.
@@ -316,14 +312,11 @@ const MainView = new Lang.Class({
         this.visible_child.cancelSearch();
     },
 
-    setPage: function(name) {
-        if (!(name in this._characterLists))
-            return;
-
-        let characterList = this.get_child_by_name(name);
+    setPage: function(category) {
+        let characterList = this.get_child_by_name(category.name);
         characterList.setFilterFont(this._filterFontFamily);
 
-        if (name == 'recent') {
+        if (category.name == 'recent') {
             if (this.recentCharacters.length == 0)
                 characterList.visible_child_name = 'empty-recent';
             else {
@@ -331,14 +324,6 @@ const MainView = new Lang.Class({
                 characterList.updateCharacterList();
             }
         } else {
-            let category = null;
-            for (let index in CategoryList.Category) {
-                category = CategoryList.Category[index];
-                if (category.name == name)
-                    break;
-            }
-
-            Util.assertNotEqual(category, null);
             characterList.searchByCategory(category);
         }
 


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