[gnome-characters/wip/dueno/local-scripts] categoryList: Add "Local scripts" tab



commit b23dec0523fbab8515663c56128c5f8b8794ef72
Author: Daiki Ueno <dueno src gnome org>
Date:   Mon Jun 15 15:19:23 2015 +0900

    categoryList: Add "Local scripts" tab
    
    https://bugzilla.gnome.org/show_bug.cgi?id=743643

 data/org.gnome.Characters.gschema.xml |   12 ++++
 lib/gc.c                              |   96 +++++++++++++++++++++++++++++---
 lib/gc.h                              |    5 ++
 src/categoryList.js                   |   42 ++++++++++++++-
 src/characterList.js                  |   16 ++++++
 src/window.js                         |    3 +
 6 files changed, 163 insertions(+), 11 deletions(-)
---
diff --git a/data/org.gnome.Characters.gschema.xml b/data/org.gnome.Characters.gschema.xml
index 30fb382..09ae962 100644
--- a/data/org.gnome.Characters.gschema.xml
+++ b/data/org.gnome.Characters.gschema.xml
@@ -14,5 +14,17 @@
       <default>100</default>
       <summary>Maximum recent characters</summary>
     </key>
+    <key name="scripts" type="as">
+      <!-- TRANSLATORS: list Unicode scripts used in your language.
+          For Latin languages, keep it empty.  -->
+      <default l10n="messages" context="scripts">[]</default>
+      <summary>A list of Unicode scripts used in this locale.</summary>
+    </key>
+    <key name="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>
+      <summary>Character representing Unicode scripts used in this locale.</summary>
+    </key>
   </schema>
 </schemalist>
diff --git a/lib/gc.c b/lib/gc.c
index 83c7c8b..a1efec5 100644
--- a/lib/gc.c
+++ b/lib/gc.c
@@ -76,7 +76,7 @@ struct GcCharacterIter
   size_t block_index;
   size_t block_count;
 
-  const uc_script_t *script;
+  const uc_script_t * const * scripts;
   uc_general_category_t category;
   const gchar * const * keywords;
 
@@ -199,20 +199,32 @@ gc_character_iter_init_for_blocks (GcCharacterIter  *iter,
 }
 
 static gboolean
-filter_script (GcCharacterIter *iter, ucs4_t uc)
+filter_scripts (GcCharacterIter *iter, ucs4_t uc)
 {
-  return uc_is_print (uc) && uc_is_script (uc, iter->script);
+  const uc_script_t * const *scripts = iter->scripts;
+
+  if (!uc_is_print (uc))
+    return FALSE;
+
+  while (*scripts)
+    {
+      if (uc_is_script (uc, *scripts))
+       return TRUE;
+      scripts++;
+    }
+
+  return FALSE;
 }
 
 static void
-gc_character_iter_init_for_script (GcCharacterIter   *iter,
-                                   const uc_script_t *script)
+gc_character_iter_init_for_scripts (GcCharacterIter   *iter,
+                                   const uc_script_t * const * scripts)
 {
   gc_character_iter_init (iter);
   iter->blocks = all_blocks;
   iter->block_count = all_block_count;
-  iter->filter = filter_script;
-  iter->script = script;
+  iter->filter = filter_scripts;
+  iter->scripts = scripts;
 }
 
 static void
@@ -327,8 +339,13 @@ gc_enumerate_character_by_category (GcCharacterIter *iter,
       return;
 
     case GC_CATEGORY_LATIN:
-      gc_character_iter_init_for_script (iter, uc_script ('A'));
-      return;
+      {
+       static const uc_script_t *latin_scripts[2];
+       latin_scripts[0] = uc_script ('A');
+       latin_scripts[1] = NULL;
+       gc_character_iter_init_for_scripts (iter, latin_scripts);
+       return;
+      }
 
     case GC_CATEGORY_EMOTICON:
       {
@@ -437,6 +454,7 @@ struct SearchData
 {
   GcCategory category;
   gchar **keywords;
+  const uc_script_t **scripts;
   gunichar uc;
   gint max_matches;
 };
@@ -446,6 +464,8 @@ search_data_free (struct SearchData *data)
 {
   if (data->keywords)
     g_strfreev (data->keywords);
+  if (data->scripts)
+    g_free (data->scripts);
   g_slice_free (struct SearchData, data);
 }
 
@@ -558,6 +578,64 @@ gc_search_by_keywords (const gchar * const * keywords,
   g_task_run_in_thread (task, gc_search_by_keywords_thread);
 }
 
+static void
+gc_search_by_scripts_thread (GTask        *task,
+                            gpointer      source_object,
+                            gpointer      task_data,
+                            GCancellable *cancellable)
+{
+  GcCharacterIter iter;
+  GArray *result;
+  struct SearchData *data = task_data;
+
+  if (!all_blocks)
+    uc_all_blocks (&all_blocks, &all_block_count);
+
+  result = g_array_new (FALSE, FALSE, sizeof (gunichar));
+  gc_character_iter_init_for_scripts (&iter,
+                                     (const uc_script_t * const *) data->scripts);
+  while (!g_cancellable_is_cancelled (cancellable)
+        && gc_character_iter_next (&iter))
+    {
+      gunichar uc = gc_character_iter_get (&iter);
+      g_array_append_val (result, uc);
+    }
+
+  g_task_return_pointer (task, result, (GDestroyNotify) g_array_unref);
+}
+
+/**
+ * gc_search_by_scripts:
+ * @scripts: (array zero-terminated=1) (element-type utf8): an array of scripts
+ * @max_matches: the maximum number of results.
+ * @cancellable: a #GCancellable.
+ * @callback: a #GAsyncReadyCallback.
+ * @user_data: a user data passed to @callback.
+ */
+void
+gc_search_by_scripts (const gchar * const * scripts,
+                     gint                  max_matches,
+                     GCancellable         *cancellable,
+                     GAsyncReadyCallback   callback,
+                     gpointer              user_data)
+{
+  GTask *task;
+  struct SearchData *data;
+  guint length, i;
+
+  task = g_task_new (NULL, cancellable, callback, user_data);
+
+  data = g_slice_new0 (struct SearchData);
+  length = g_strv_length ((gchar **) scripts);
+  data->scripts = g_malloc0_n (length + 1, sizeof (uc_script_t *));
+  for (i = 0; i < length; i++)
+    data->scripts[i] = uc_script_byname (scripts[i]);
+  data->max_matches = max_matches;
+  g_task_set_task_data (task, data,
+                       (GDestroyNotify) search_data_free);
+  g_task_run_in_thread (task, gc_search_by_scripts_thread);
+}
+
 static int
 confusable_character_class_compare (const void *a,
                                    const void *b)
diff --git a/lib/gc.h b/lib/gc.h
index a735e11..6383cdd 100644
--- a/lib/gc.h
+++ b/lib/gc.h
@@ -49,6 +49,11 @@ void            gc_search_by_keywords     (const gchar * const * keywords,
                                            GCancellable         *cancellable,
                                            GAsyncReadyCallback   callback,
                                            gpointer              user_data);
+void            gc_search_by_scripts      (const gchar * const * scripts,
+                                           gint                  max_matches,
+                                           GCancellable         *cancellable,
+                                           GAsyncReadyCallback   callback,
+                                           gpointer              user_data);
 void            gc_search_related         (gunichar              uc,
                                            gint                  max_matches,
                                            GCancellable         *cancellable,
diff --git a/src/categoryList.js b/src/categoryList.js
index 82beff7..c1f90cc 100644
--- a/src/categoryList.js
+++ b/src/categoryList.js
@@ -18,11 +18,15 @@
 
 const Lang = imports.lang;
 const Params = imports.params;
+const Cairo = imports.cairo;
+const PangoCairo = imports.gi.PangoCairo;
+const Pango = imports.gi.Pango;
 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 Category = [
     {
@@ -78,6 +82,11 @@ const Category = [
         category: Gc.Category.EMOTICON,
         title: N_('Emoticons'),
         icon_name: 'face-smile-symbolic'
+    },
+    {
+        name: 'local',
+        category: Gc.Category.NONE,
+        title: N_('Local scripts')
     }
 ];
 
@@ -97,8 +106,13 @@ const CategoryListRowWidget = new Lang.Class({
                                  margin_start: 10 });
         this.add(hbox);
 
-        let icon = new Gio.ThemedIcon({ name: category.icon_name });
-        let image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.LARGE_TOOLBAR);
+        let image;
+        if (category.name == 'local') {
+            image = this._createScriptsImage();
+        } else {
+            let icon = new Gio.ThemedIcon({ name: category.icon_name });
+            image = Gtk.Image.new_from_gicon(icon, Gtk.IconSize.LARGE_TOOLBAR);
+        }
         image.get_style_context().add_class('category-image');
         hbox.pack_start(image, false, false, 2);
 
@@ -106,6 +120,25 @@ const CategoryListRowWidget = new Lang.Class({
                                     halign: Gtk.Align.START });
         label.get_style_context().add_class('category-label');
         hbox.pack_start(label, true, true, 0);
+    },
+
+    _createScriptsImage: function() {
+        let surface = new Cairo.ImageSurface(Cairo.Format.ARGB32, 24, 24);
+        let cr = new CairoContext(surface);
+        let layout = PangoCairo.create_layout(cr);
+
+        let context = this.get_pango_context();
+        layout.get_context().set_font_map(context.get_font_map());
+        let fontDescription = context.get_font_description();
+        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);
+        PangoCairo.show_layout(cr, layout);
+
+        return Gtk.Image.new_from_surface(surface);
     }
 });
 
@@ -120,8 +153,13 @@ 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 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 4dbdf59..4d3ec08 100644
--- a/src/characterList.js
+++ b/src/characterList.js
@@ -376,6 +376,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 e3eea97..59f39e4 100644
--- a/src/window.js
+++ b/src/window.js
@@ -330,6 +330,9 @@ 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) {


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