[evolution] Bug 561799 - Simplify language selection in message composer



commit 6a0409a985978c5fa5e68e686eb04b36f1faf71c
Author: Milan Crha <mcrha redhat com>
Date:   Tue Oct 30 11:01:23 2018 +0100

    Bug 561799 - Simplify language selection in message composer
    
    Closes https://bugzilla.gnome.org/show_bug.cgi?id=561799

 data/org.gnome.evolution.mail.gschema.xml.in |   9 ++
 src/e-util/e-html-editor-actions.c           | 166 ++++++++++++++++++++++++++-
 src/e-util/e-html-editor-manager.ui          |   6 +-
 src/e-util/e-html-editor-private.h           |   1 +
 src/e-util/e-misc-utils.c                    | 116 +++++++++++++------
 src/e-util/e-misc-utils.h                    |   3 +
 6 files changed, 261 insertions(+), 40 deletions(-)
---
diff --git a/data/org.gnome.evolution.mail.gschema.xml.in b/data/org.gnome.evolution.mail.gschema.xml.in
index a9e0e2c852..9c2504633c 100644
--- a/data/org.gnome.evolution.mail.gschema.xml.in
+++ b/data/org.gnome.evolution.mail.gschema.xml.in
@@ -125,6 +125,15 @@
       <_summary>Spell checking languages</_summary>
       <_description>List of dictionary language codes used for spell checking.</_description>
     </key>
+    <key name="composer-spell-languages-recently-used" type="as">
+      <default>[]</default>
+      <_summary>List of recently used spell checking languages</_summary>
+      <_description>List of dictionary language codes used for spell checking, which had been used 
recently.</_description>
+    </key>
+    <key name="composer-spell-languages-max-recently-used" type="i">
+      <default>5</default>
+      <_summary>How many recently used spell checking languages to remember</_summary>
+    </key>
     <key name="composer-show-bcc" type="b">
       <default>false</default>
       <_summary>Show “Bcc” field when sending a mail message</_summary>
diff --git a/src/e-util/e-html-editor-actions.c b/src/e-util/e-html-editor-actions.c
index 8f7eeceed4..f2cd27636d 100644
--- a/src/e-util/e-html-editor-actions.c
+++ b/src/e-util/e-html-editor-actions.c
@@ -440,6 +440,10 @@ action_insert_text_file_cb (GtkAction *action,
        gtk_widget_destroy (dialog);
 }
 
+static gboolean
+editor_actions_add_to_recent_languages (EHTMLEditor *editor,
+                                       const gchar *language_code);
+
 static void
 action_language_cb (GtkToggleAction *toggle_action,
                     EHTMLEditor *editor)
@@ -468,6 +472,41 @@ action_language_cb (GtkToggleAction *toggle_action,
        e_html_editor_update_spell_actions (editor);
 
        g_signal_emit_by_name (editor, "spell-languages-changed");
+
+       if (active) {
+               GSettings *settings;
+               GPtrArray *array;
+               gchar **strv;
+               gint ii, max_items;
+
+               gtk_ui_manager_remove_ui (editor->priv->manager, 
editor->priv->recent_spell_languages_merge_id);
+
+               settings = e_util_ref_settings ("org.gnome.evolution.mail");
+               strv = g_settings_get_strv (settings, "composer-spell-languages-recently-used");
+               max_items = g_settings_get_int (settings, "composer-spell-languages-max-recently-used");
+               if (max_items < 5)
+                       max_items = 5;
+
+               array = g_ptr_array_sized_new (max_items + 1);
+               g_ptr_array_add (array, (gpointer) language_code);
+
+               editor_actions_add_to_recent_languages (editor, language_code);
+
+               for (ii = 0; strv && strv[ii] && array->len < max_items; ii++) {
+                       if (g_strcmp0 (language_code, strv[ii]) != 0) {
+                               g_ptr_array_add (array, strv[ii]);
+                               editor_actions_add_to_recent_languages (editor, strv[ii]);
+                       }
+               }
+
+               g_ptr_array_add (array, NULL);
+
+               g_settings_set_strv (settings, "composer-spell-languages-recently-used", (const gchar * const 
*) array->pdata);
+
+               g_object_unref (settings);
+               g_ptr_array_free (array, TRUE);
+               g_strfreev (strv);
+       }
 }
 
 static gboolean
@@ -1606,6 +1645,49 @@ static GtkActionEntry spell_context_entries[] = {
          NULL }
 };
 
+static gboolean
+editor_actions_add_to_recent_languages (EHTMLEditor *editor,
+                                       const gchar *language_code)
+{
+       GtkAction *language_action;
+       gchar *name;
+
+       g_return_val_if_fail (E_IS_HTML_EDITOR (editor), FALSE);
+       g_return_val_if_fail (language_code != NULL, FALSE);
+
+       language_action = gtk_action_group_get_action (editor->priv->language_actions, language_code);
+       if (!language_action)
+               return FALSE;
+
+       name = g_strconcat ("recent-spell-language-", language_code, NULL);
+
+       if (!gtk_action_group_get_action (editor->priv->language_actions, name)) {
+               GtkToggleAction *toggle_action;
+
+               toggle_action = gtk_toggle_action_new (name,
+                       gtk_action_get_label (language_action),
+                       gtk_action_get_tooltip (language_action),
+                       NULL);
+
+               e_binding_bind_property (language_action, "active",
+                       toggle_action, "active",
+                       G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
+
+               gtk_action_group_add_action (editor->priv->language_actions, GTK_ACTION (toggle_action));
+
+               g_object_unref (toggle_action);
+       }
+
+       gtk_ui_manager_add_ui (
+               editor->priv->manager, editor->priv->recent_spell_languages_merge_id,
+               "/main-menu/edit-menu/language-menu/recent-languages",
+               name, name, GTK_UI_MANAGER_AUTO, FALSE);
+
+       g_free (name);
+
+       return TRUE;
+}
+
 static void
 editor_actions_setup_languages_menu (EHTMLEditor *editor)
 {
@@ -1613,31 +1695,52 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
        EContentEditor *cnt_editor;
        GtkUIManager *manager;
        GtkActionGroup *action_group;
+       GHashTable *lang_parents; /* gchar *name ~> GtkAction * */
        GList *list = NULL, *link;
+       GSettings *settings;
+       gchar **strv;
+       gint ii, added = 0, max_items;
        guint merge_id;
 
+       lang_parents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
        manager = editor->priv->manager;
        action_group = editor->priv->language_actions;
        cnt_editor = e_html_editor_get_content_editor (editor);
        spell_checker = e_content_editor_ref_spell_checker (cnt_editor);
        merge_id = gtk_ui_manager_new_merge_id (manager);
+       editor->priv->recent_spell_languages_merge_id = gtk_ui_manager_new_merge_id (manager);
 
        list = e_spell_checker_list_available_dicts (spell_checker);
 
        for (link = list; link != NULL; link = g_list_next (link)) {
                ESpellDictionary *dictionary = link->data;
+               GtkAction *parent_action;
                GtkToggleAction *action;
-               const gchar *language_name;
+               const gchar *dictionay_name;
+               gchar *language_name, *path;
                GString *escaped_name = NULL;
                gboolean active = FALSE;
 
-               language_name = e_spell_dictionary_get_name (dictionary);
-               if (language_name && strchr (language_name, '_') != NULL)
-                       escaped_name = e_str_replace_string (language_name, "_", "__");
+               if (!e_util_get_language_info (e_spell_dictionary_get_code (dictionary), &language_name, 
NULL)) {
+                       language_name = g_strdup (e_spell_dictionary_get_code (dictionary));
+                       if (language_name) {
+                               gchar *ptr;
+
+                               ptr = strchr (language_name, '_');
+                               if (ptr)
+                                       *ptr = '\0';
+                       } else {
+                               language_name = g_strdup ("");
+                       }
+               }
+
+               dictionay_name = e_spell_dictionary_get_name (dictionary);
+               if (dictionay_name && strchr (dictionay_name, '_') != NULL)
+                       escaped_name = e_str_replace_string (dictionay_name, "_", "__");
 
                action = gtk_toggle_action_new (
                        e_spell_dictionary_get_code (dictionary),
-                       escaped_name ? escaped_name->str : language_name,
+                       escaped_name ? escaped_name->str : dictionay_name,
                        NULL, NULL);
 
                if (escaped_name)
@@ -1659,16 +1762,63 @@ editor_actions_setup_languages_menu (EHTMLEditor *editor)
 
                g_object_unref (action);
 
+               parent_action = g_hash_table_lookup (lang_parents, language_name);
+               if (!parent_action) {
+                       gchar *name, *tmp;
+
+                       name = g_strdup (e_spell_dictionary_get_code (dictionary));
+                       tmp = strchr (name, '_');
+                       if (tmp)
+                               *tmp = '\0';
+
+                       tmp = g_strconcat ("language-parent-", name, NULL);
+                       g_free (name);
+                       name = tmp;
+
+                       parent_action = gtk_action_new (name, language_name, NULL, NULL);
+
+                       gtk_action_group_add_action (action_group, parent_action);
+
+                       g_hash_table_insert (lang_parents, g_strdup (language_name), parent_action);
+
+                       gtk_ui_manager_add_ui (
+                               manager, merge_id,
+                               "/main-menu/edit-menu/language-menu/all-languages",
+                               name, name, GTK_UI_MANAGER_MENU, FALSE);
+
+                       g_free (name);
+               }
+
+               path = g_strconcat ("/main-menu/edit-menu/language-menu/all-languages/", gtk_action_get_name 
(parent_action), NULL);
+
                gtk_ui_manager_add_ui (
                        manager, merge_id,
-                       "/main-menu/edit-menu/language-menu",
+                       path,
                        e_spell_dictionary_get_code (dictionary),
                        e_spell_dictionary_get_code (dictionary),
                        GTK_UI_MANAGER_AUTO, FALSE);
+
+               g_free (language_name);
+               g_free (path);
        }
 
        g_list_free (list);
        g_clear_object (&spell_checker);
+       g_hash_table_destroy (lang_parents);
+
+       settings = e_util_ref_settings ("org.gnome.evolution.mail");
+       strv = g_settings_get_strv (settings, "composer-spell-languages-recently-used");
+       max_items = g_settings_get_int (settings, "composer-spell-languages-max-recently-used");
+       if (max_items < 5)
+               max_items = 5;
+       g_object_unref (settings);
+
+       for (ii = 0; strv && strv[ii] && added < max_items; ii++) {
+               if (editor_actions_add_to_recent_languages (editor, strv[ii]))
+                       added++;
+       }
+
+       g_strfreev (strv);
 }
 
 static void
@@ -2024,6 +2174,10 @@ editor_actions_update_spellcheck_languages_menu (EHTMLEditor *editor,
                if (!GTK_IS_TOGGLE_ACTION (link->data))
                        continue;
 
+               if (gtk_action_get_name (link->data) &&
+                   g_str_has_prefix (gtk_action_get_name (link->data), "recent-spell-language-"))
+                       continue;
+
                is_active = g_hash_table_contains (active, gtk_action_get_name (link->data));
                toggle_action = GTK_TOGGLE_ACTION (link->data);
 
diff --git a/src/e-util/e-html-editor-manager.ui b/src/e-util/e-html-editor-manager.ui
index fa7bc0bbbf..487dce6be0 100644
--- a/src/e-util/e-html-editor-manager.ui
+++ b/src/e-util/e-html-editor-manager.ui
@@ -22,7 +22,11 @@
       <separator/>
       <placeholder name='pre-spell-check'/>
       <menuitem action='spell-check'/>
-      <menu action='language-menu'/>
+      <menu action='language-menu'>
+        <placeholder name='recent-languages'/>
+        <separator/>
+        <placeholder name='all-languages'/>
+      </menu>
     </menu>
     <placeholder name='pre-insert-menu'>
       <menu action='view-menu'>
diff --git a/src/e-util/e-html-editor-private.h b/src/e-util/e-html-editor-private.h
index b652f0a538..ec068f4ae6 100644
--- a/src/e-util/e-html-editor-private.h
+++ b/src/e-util/e-html-editor-private.h
@@ -89,6 +89,7 @@ struct _EHTMLEditorPrivate {
        gchar *filename;
 
        guint spell_suggestions_merge_id;
+       guint recent_spell_languages_merge_id;
 
        gint editor_layout_row;
 
diff --git a/src/e-util/e-misc-utils.c b/src/e-util/e-misc-utils.c
index 21896b470b..8c7e5c544d 100644
--- a/src/e-util/e-misc-utils.c
+++ b/src/e-util/e-misc-utils.c
@@ -4416,30 +4416,49 @@ iso_codes_parse (const GMarkupParser *parser,
 #endif /* HAVE_ISO_CODES */
 
 /**
- * e_util_get_language_name:
- * @language_tag: Language tag to get its name for
+ * e_util_get_language_info:
+ * @language_tag: Language tag to get its name for, like "en_US"
+ * @out_language_name: (out) (nullable) (transfer full): Return location for the language name, or %NULL
+ * @out_country_name: (out) (nullable) (transfer full): Return location for the country name, or %NULL
  *
- * Returns: (transfer full): Newly allocated string with localized language name
+ * Splits language tag into a localized language name and country name (the variant).
+ * The @out_language_name is always filled when the function returns %TRUE, but
+ * the @out_countr_name can be %NULL. That's for cases when the @language_tag
+ * contains only the country part, like "en".
+ *
+ * The function returns %FALSE when it could not decode language name from
+ * the given @language_tag. When either of the @out_language_name and @out_country_name
+ * is non-NULL and the function returns %TRUE, then their respective values
+ * should be freed with g_free(), when no longer needed.
+ *
+ * Returns: %TRUE, when could get at least language name from the @language_tag,
+ *    %FALSE otherwise.
  *
  * Since: 3.32
  **/
-gchar *
-e_util_get_language_name (const gchar *language_tag)
+gboolean
+e_util_get_language_info (const gchar *language_tag,
+                         gchar **out_language_name,
+                         gchar **out_country_name)
 {
        const gchar *iso_639_name;
        const gchar *iso_3166_name;
-       gchar *language_name;
        gchar *lowercase;
        gchar **tokens;
 
-       g_return_val_if_fail (language_tag != NULL, NULL);
+       g_return_val_if_fail (language_tag != NULL, FALSE);
+
+       if (out_language_name)
+               *out_language_name = NULL;
+       if (out_country_name)
+               *out_country_name = NULL;
 
        /* Split language code into lowercase tokens. */
        lowercase = g_ascii_strdown (language_tag, -1);
        tokens = g_strsplit (lowercase, "_", -1);
        g_free (lowercase);
 
-       g_return_val_if_fail (tokens != NULL, NULL);
+       g_return_val_if_fail (tokens != NULL, FALSE);
 
        if (!iso_639_table && !iso_3166_table) {
 #if defined (ENABLE_NLS) && defined (HAVE_ISO_CODES)
@@ -4472,46 +4491,77 @@ e_util_get_language_name (const gchar *language_tag)
 
        iso_639_name = g_hash_table_lookup (iso_639_table, tokens[0]);
 
-       if (iso_639_name == NULL) {
-               language_name = g_strdup_printf (
-               /* Translators: %s is the language ISO code. */
-                       C_("language", "Unknown (%s)"), language_tag);
-               goto exit;
+       if (!iso_639_name) {
+               g_strfreev (tokens);
+               return FALSE;
        }
 
-       if (g_strv_length (tokens) < 2) {
-               language_name = g_strdup (iso_639_name);
+       if (out_language_name)
+               *out_language_name = g_strdup (iso_639_name);
+
+       if (g_strv_length (tokens) < 2)
                goto exit;
-       }
 
-       iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
+       if (out_country_name) {
+               iso_3166_name = g_hash_table_lookup (iso_3166_table, tokens[1]);
 
-       if (iso_3166_name != NULL)
-               language_name = g_strdup_printf (
-                /* Translators: The first %s is the language name, and the
-                * second is the country name. Example: "French (France)" */
-                       C_("language", "%s (%s)"), iso_639_name, iso_3166_name);
-       else
-               language_name = g_strdup_printf (
-                /* Translators: The first %s is the language name, and the
-                * second is the country name. Example: "French (France)" */
-                       C_("language", "%s (%s)"), iso_639_name, tokens[1]);
+               if (iso_3166_name)
+                       *out_country_name = g_strdup (iso_3166_name);
+               else
+                       *out_country_name = g_strdup (tokens[1]);
+       }
 
  exit:
-       g_strfreev (tokens);
-
-       if (language_name) {
+       if (out_country_name && *out_country_name) {
                gchar *ptr;
 
-               /* When the name has two or more ';' then strip the string at the second of them */
-               ptr = strchr (language_name, ';');
+               /* When the country name has two or more ';' then strip the string at the second of them */
+               ptr = strchr (*out_country_name, ';');
                if (ptr)
                        ptr = strchr (ptr + 1, ';');
                if (ptr)
                        *ptr = '\0';
        }
 
-       return language_name;
+       g_strfreev (tokens);
+
+       return TRUE;
+}
+
+/**
+ * e_util_get_language_name:
+ * @language_tag: Language tag to get its name for, like "en_US"
+ *
+ * Returns: (transfer full): Newly allocated string with localized language name
+ *
+ * Since: 3.32
+ **/
+gchar *
+e_util_get_language_name (const gchar *language_tag)
+{
+       gchar *language_name = NULL, *country_name = NULL;
+       gchar *res;
+
+       g_return_val_if_fail (language_tag != NULL, NULL);
+
+       if (!e_util_get_language_info (language_tag, &language_name, &country_name)) {
+               return g_strdup_printf (
+                       /* Translators: %s is the language ISO code. */
+                       C_("language", "Unknown (%s)"), language_tag);
+       }
+
+       if (!country_name)
+               return language_name;
+
+       res = g_strdup_printf (
+               /* Translators: The first %s is the language name, and the
+                * second is the country name. Example: "French (France)" */
+               C_("language", "%s (%s)"), language_name, country_name);
+
+       g_free (language_name);
+       g_free (country_name);
+
+       return res;
 }
 
 /**
diff --git a/src/e-util/e-misc-utils.h b/src/e-util/e-misc-utils.h
index 3b8e976758..8c5551b246 100644
--- a/src/e-util/e-misc-utils.h
+++ b/src/e-util/e-misc-utils.h
@@ -348,6 +348,9 @@ gboolean    e_util_query_ldap_root_dse_sync (const gchar *host,
                                                 GCancellable *cancellable,
                                                 GError **error);
 gchar *                e_util_get_language_name        (const gchar *language_tag);
+gboolean       e_util_get_language_info        (const gchar *language_tag,
+                                                gchar **out_language_name,
+                                                gchar **out_country_name);
 void           e_misc_util_free_global_memory  (void);
 
 G_END_DECLS


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