[PATCH 10/14] Rework the Langsupport spoke to work with all locales

Vratislav Podzimek vpodzime at redhat.com
Wed Sep 25 17:27:53 UTC 2013


This is a follow up for the Welcome spoke reworking.

Related: rhbz#1006458
(cherry-picked f41e79245666755a45b575900460f730aee9a52b from master)

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 pyanaconda/ui/gui/spokes/langsupport.glade | 166 ++++++++++++++------
 pyanaconda/ui/gui/spokes/langsupport.py    | 241 +++++++++++++++++------------
 2 files changed, 263 insertions(+), 144 deletions(-)

diff --git a/pyanaconda/ui/gui/spokes/langsupport.glade b/pyanaconda/ui/gui/spokes/langsupport.glade
index afb5995..6727c9b 100644
--- a/pyanaconda/ui/gui/spokes/langsupport.glade
+++ b/pyanaconda/ui/gui/spokes/langsupport.glade
@@ -2,7 +2,7 @@
 <interface>
   <!-- interface-requires gtk+ 3.0 -->
   <!-- interface-requires AnacondaWidgets 1.0 -->
-  <object class="GtkListStore" id="langsupportStore">
+  <object class="GtkListStore" id="languageStore">
     <columns>
       <!-- column-name nativeName -->
       <column type="gchararray"/>
@@ -10,14 +10,10 @@
       <column type="gchararray"/>
       <!-- column-name langSetting -->
       <column type="gchararray"/>
-      <!-- column-name langSupportSelected -->
-      <column type="gboolean"/>
-      <!-- column-name isAdditional -->
-      <column type="gboolean"/>
     </columns>
   </object>
-  <object class="GtkTreeModelFilter" id="langsupportStoreFilter">
-    <property name="child_model">langsupportStore</property>
+  <object class="GtkTreeModelFilter" id="languageStoreFilter">
+    <property name="child_model">languageStore</property>
   </object>
   <object class="AnacondaSpokeWindow" id="langsupportWindow">
     <property name="startup_id">filler</property>
@@ -82,54 +78,47 @@
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkBox" id="box1">
+                  <object class="GtkGrid" id="mainGrid">
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
+                    <property name="halign">center</property>
                     <property name="hexpand">True</property>
                     <property name="vexpand">True</property>
-                    <property name="orientation">vertical</property>
+                    <property name="row_spacing">6</property>
+                    <property name="column_spacing">6</property>
                     <child>
-                      <object class="GtkScrolledWindow" id="langsupportScrolledwindow1">
+                      <object class="GtkScrolledWindow" id="languageWindow">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="hexpand">True</property>
                         <property name="vexpand">True</property>
+                        <property name="hscrollbar_policy">never</property>
                         <property name="shadow_type">in</property>
                         <child>
-                          <object class="GtkTreeView" id="langsupportView">
+                          <object class="GtkTreeView" id="languageView">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
                             <property name="hexpand">True</property>
                             <property name="vexpand">True</property>
-                            <property name="model">langsupportStoreFilter</property>
+                            <property name="model">languageStoreFilter</property>
                             <property name="headers_visible">False</property>
                             <property name="headers_clickable">False</property>
+                            <property name="enable_search">False</property>
                             <property name="search_column">0</property>
                             <child internal-child="selection">
-                              <object class="GtkTreeSelection" id="langsupportViewSelection"/>
-                            </child>
-                            <child>
-                              <object class="GtkTreeViewColumn" id="langsupportSelectedCol">
-                                <property name="title" translatable="yes">column</property>
-                                <signal name="clicked" handler="on_langsupport_toggled" swapped="no"/>
-                                <child>
-                                  <object class="GtkCellRendererToggle" id="langsupportSelectedRenderer">
-                                    <signal name="toggled" handler="on_langsupport_toggled" swapped="no"/>
-                                  </object>
-                                  <attributes>
-                                    <attribute name="sensitive">4</attribute>
-                                    <attribute name="activatable">4</attribute>
-                                    <attribute name="active">3</attribute>
-                                  </attributes>
-                                </child>
+                              <object class="GtkTreeSelection" id="languageViewSelection">
+                                <signal name="changed" handler="on_lang_selection_changed" swapped="no"/>
                               </object>
                             </child>
                             <child>
-                              <object class="GtkTreeViewColumn" id="nativeNameCol">
-                                <property name="title" translatable="yes">column</property>
+                              <object class="GtkTreeViewColumn" id="nativeName">
+                                <property name="title" translatable="yes">nativeName</property>
                                 <property name="expand">True</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_column_id">0</property>
                                 <child>
                                   <object class="GtkCellRendererText" id="nativeNameRenderer">
+                                    <property name="xalign">0.89999997615814209</property>
                                     <property name="font">Cantarell 12</property>
                                   </object>
                                   <attributes>
@@ -139,52 +128,127 @@
                               </object>
                             </child>
                             <child>
-                              <object class="GtkTreeViewColumn" id="englishNameCol">
-                                <property name="title" translatable="yes">column</property>
+                              <object class="GtkTreeViewColumn" id="englishName">
+                                <property name="title" translatable="yes">englishName</property>
                                 <property name="expand">True</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_column_id">1</property>
                                 <child>
                                   <object class="GtkCellRendererText" id="englishNameRenderer">
+                                    <property name="xalign">0.10000000149011612</property>
                                     <property name="font">Cantarell Italic 14</property>
                                     <property name="foreground">gray</property>
                                   </object>
                                   <attributes>
-                                    <attribute name="markup">1</attribute>
+                                    <attribute name="text">1</attribute>
                                   </attributes>
                                 </child>
                               </object>
                             </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="langSelectedColumn">
+                                <property name="title" translatable="yes">selected</property>
+                                <child>
+                                  <object class="GtkCellRendererPixbuf" id="langSelectedRenderer"/>
+                                </child>
+                              </object>
+                            </child>
                           </object>
                         </child>
                       </object>
                       <packing>
-                        <property name="expand">True</property>
-                        <property name="fill">True</property>
-                        <property name="position">1</property>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
                       </packing>
                     </child>
                     <child>
-                      <object class="GtkEntry" id="langsupportEntry">
+                      <object class="GtkEntry" id="languageEntry">
                         <property name="visible">True</property>
                         <property name="can_focus">True</property>
                         <property name="valign">start</property>
-                        <property name="margin_top">6</property>
-                        <property name="hexpand">True</property>
                         <property name="invisible_char">●</property>
                         <property name="invisible_char_set">True</property>
                         <property name="secondary_icon_name">edit-clear-symbolic</property>
-                        <property name="placeholder_text" translatable="yes">Type here to search.</property>
+                        <property name="placeholder_text">Type here to search.</property>
                         <signal name="changed" handler="on_entry_changed" swapped="no"/>
-                        <signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/>
                       </object>
                       <packing>
-                        <property name="expand">False</property>
-                        <property name="fill">True</property>
-                        <property name="position">2</property>
+                        <property name="left_attach">0</property>
+                        <property name="top_attach">1</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkScrolledWindow" id="localeWindow">
+                        <property name="height_request">150</property>
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="hexpand">True</property>
+                        <property name="vexpand">True</property>
+                        <property name="hscrollbar_policy">never</property>
+                        <property name="shadow_type">in</property>
+                        <child>
+                          <object class="GtkTreeView" id="localeView">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="hexpand">True</property>
+                            <property name="vexpand">True</property>
+                            <property name="model">localeStore</property>
+                            <property name="headers_visible">False</property>
+                            <property name="enable_search">False</property>
+                            <property name="search_column">0</property>
+                            <child internal-child="selection">
+                              <object class="GtkTreeSelection" id="localeViewSelection"/>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="checked">
+                                <property name="title" translatable="yes">checked</property>
+                                <child>
+                                  <object class="GtkCellRendererToggle" id="checkedRenderer">
+                                    <signal name="toggled" handler="on_locale_toggled" swapped="no"/>
+                                  </object>
+                                  <attributes>
+                                    <attribute name="active">2</attribute>
+                                    <attribute name="sensitive">3</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="localeNativeName">
+                                <property name="title" translatable="yes">nativeName</property>
+                                <property name="expand">True</property>
+                                <property name="clickable">True</property>
+                                <property name="sort_column_id">0</property>
+                                <child>
+                                  <object class="GtkCellRendererText" id="localeNativeNameRenderer">
+                                    <property name="font">Cantarell 12</property>
+                                  </object>
+                                  <attributes>
+                                    <attribute name="markup">0</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="left_attach">1</property>
+                        <property name="top_attach">0</property>
+                        <property name="width">1</property>
+                        <property name="height">1</property>
                       </packing>
                     </child>
+                    <child>
+                      <placeholder/>
+                    </child>
                   </object>
                   <packing>
-                    <property name="expand">True</property>
+                    <property name="expand">False</property>
                     <property name="fill">True</property>
                     <property name="position">1</property>
                   </packing>
@@ -204,4 +268,16 @@
       </object>
     </child>
   </object>
+  <object class="GtkListStore" id="localeStore">
+    <columns>
+      <!-- column-name nativeName -->
+      <column type="gchararray"/>
+      <!-- column-name locale -->
+      <column type="gchararray"/>
+      <!-- column-name selected -->
+      <column type="gboolean"/>
+      <!-- column-name isAdditional -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
 </interface>
diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py
index 1019457..dadcab8 100644
--- a/pyanaconda/ui/gui/spokes/langsupport.py
+++ b/pyanaconda/ui/gui/spokes/langsupport.py
@@ -17,6 +17,7 @@
 # Red Hat, Inc.
 #
 # Red Hat Author(s): Radek Vykydal <rvykydal at redhat.com>
+#                    Vratislav Podzimek <vpodzime at redhat.com>
 #
 
 # pylint: disable-msg=E0611
@@ -26,6 +27,7 @@ from pyanaconda.i18n import _, N_
 from pyanaconda.iutil import strip_accents
 from pyanaconda.ui.gui.spokes import NormalSpoke
 from pyanaconda.ui.gui.categories.localization import LocalizationCategory
+from pyanaconda.ui.gui.utils import set_treeview_selection
 from pyanaconda import localization
 
 import re
@@ -35,14 +37,8 @@ log = logging.getLogger("anaconda")
 
 __all__ = ["LangsupportSpoke"]
 
-COL_NATIVE_NAME   = 0
-COL_ENGLISH_NAME  = 1
-COL_LANG_SETTING  = 2
-COL_SELECTED      = 3
-COL_IS_ADDITIONAL = 4
-
 class LangsupportSpoke(NormalSpoke):
-    builderObjects = ["langsupportStore", "langsupportStoreFilter", "langsupportWindow"]
+    builderObjects = ["languageStore", "languageStoreFilter", "localeStore", "langsupportWindow"]
     mainWidgetName = "langsupportWindow"
     uiFile = "spokes/langsupport.glade"
 
@@ -53,60 +49,59 @@ class LangsupportSpoke(NormalSpoke):
 
     def __init__(self, *args, **kwargs):
         NormalSpoke.__init__(self, *args, **kwargs)
+        self._selected_locales = set()
 
     def initialize(self):
-        self._langsupportStore = self.builder.get_object("langsupportStore")
-        self._langsupportEntry = self.builder.get_object("langsupportEntry")
-        self._langsupportStoreFilter = self.builder.get_object("langsupportStoreFilter")
-        self._langsupportStoreFilter.set_visible_func(self._matches_entry, None)
-
-        # mark selected items in language list bold
-        for col, rend, idx in (("englishNameCol", "englishNameRenderer", COL_ENGLISH_NAME),
-                               ("nativeNameCol", "nativeNameRenderer", COL_NATIVE_NAME)):
+        self._languageStore = self.builder.get_object("languageStore")
+        self._languageEntry = self.builder.get_object("languageEntry")
+        self._languageStoreFilter = self.builder.get_object("languageStoreFilter")
+        self._langView = self.builder.get_object("languageView")
+        self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer")
+        self._langSelectedColumn = self.builder.get_object("langSelectedColumn")
+        self._langSelection = self.builder.get_object("languageViewSelection")
+        self._localeStore = self.builder.get_object("localeStore")
+        self._localeView = self.builder.get_object("localeView")
+
+        # fill the list with available translations
+        for lang in localization.get_available_translations():
+            self._add_language(self._languageStore,
+                               localization.get_native_name(lang),
+                               localization.get_english_name(lang), lang)
+
+        # render a right arrow for the chosen language
+        self._right_arrow = Gtk.Image.new_from_file("/usr/share/anaconda/pixmaps/right-arrow-icon.png")
+        self._langSelectedColumn.set_cell_data_func(self._langSelectedRenderer,
+                                                    self._render_lang_selected)
+
+        # mark selected locales and languages with selected locales bold
+        localeNativeColumn = self.builder.get_object("localeNativeName")
+        localeNativeNameRenderer = self.builder.get_object("localeNativeNameRenderer")
+        localeNativeColumn.set_cell_data_func(localeNativeNameRenderer,
+                                              self._mark_selected_locale_bold)
+
+        for col, rend in [("nativeName", "nativeNameRenderer"),
+                          ("englishName", "englishNameRenderer")]:
             column = self.builder.get_object(col)
             renderer = self.builder.get_object(rend)
-            column.set_cell_data_func(renderer, self._mark_selected_bold, idx)
-
-        # source of lang code <-> UI name mapping
-        # (localization.get_available_translations() returns a generator)
-        self.locale_infos_for_data = list(localization.get_available_translations())
-        self.locale_infos_for_ui = self.locale_infos_for_data[:]
+            column.set_cell_data_func(renderer, self._mark_selected_language_bold)
 
-        for locale in sorted(self.locale_infos_for_ui):
-            self._add_language(self._langsupportStore,
-                               localization.get_native_name(locale),
-                               localization.get_english_name(locale),
-                               locale,
-                               False, True)
-
-        self._select_language(self._langsupportStore, self.data.lang.lang)
+        # make filtering work
+        self._languageStoreFilter.set_visible_func(self._matches_entry, None)
 
     def apply(self):
-        self.data.lang.addsupport = [row[COL_LANG_SETTING]
-                                     for row in self._langsupportStore
-                                     if row[COL_SELECTED] and row[COL_IS_ADDITIONAL]]
+        # store only additional langsupport locales
+        self.data.lang.addsupport = sorted(self._selected_locales - set([self.data.lang.lang]))
 
     def refresh(self):
-        self._langsupportEntry.set_text("")
-        lang_infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_ui)
-        if len(lang_infos) > 1:
-            log.warning("Found multiple locales for lang %s: %s, picking first" %
-                        (self.data.lang.lang, lang_infos))
-        # Just take the first found
-        # TODO - for corner cases choose the one that is common prefix
-        lang_infos = lang_infos[:1]
-
-        addsupp_infos = []
-        for code in self.data.lang.addsupport:
-            code_infos = self._find_localeinfos_for_code(code, self.locale_infos_for_ui)
-            addsupp_infos.extend(code_infos)
-
-        for row in self._langsupportStore:
-            if row[COL_LANG_SETTING] in addsupp_infos:
-                row[COL_SELECTED] = True
-            if row[COL_LANG_SETTING] in lang_infos:
-                row[COL_SELECTED] = True
-                row[COL_IS_ADDITIONAL] = False
+        self._languageEntry.set_text("")
+        self._selected_locales = set(self._installed_langsupports)
+
+        # select the first locale from the "to be installed" langsupports
+        self._select_locale(self._installed_langsupports[0])
+
+    @property
+    def _installed_langsupports(self):
+        return [self.data.lang.lang] + sorted(self.data.lang.addsupport)
 
     @property
     def showable(self):
@@ -114,13 +109,8 @@ class LangsupportSpoke(NormalSpoke):
 
     @property
     def status(self):
-        # TODO: translate
-        infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1]
-        for code in self.data.lang.addsupport:
-            for info in self._find_localeinfos_for_code(code, self.locale_infos_for_data):
-                if info not in infos:
-                    infos.append(info)
-        return ", ".join(localization.get_english_name(info) for info in infos)
+        return ", ".join(localization.get_native_name(locale)
+                         for locale in self._installed_langsupports)
 
     @property
     def mandatory(self):
@@ -130,41 +120,63 @@ class LangsupportSpoke(NormalSpoke):
     def completed(self):
         return True
 
-    def _find_localeinfos_for_code(self, code, infos):
-        if code in infos:
-            return [code]
-        else:
-            retval = [info for info in infos
-                      if code in localization.expand_langs(info)]
-            log.debug("locale infos found for %s: %s" % (code, retval))
-            return retval
-
-    def _add_language(self, store, native, english, setting, selected, additional):
-        store.append(['<span lang="%s">%s</span>' % (re.sub(r'\..*', '', setting), native),
-                     english, setting, selected, additional])
-
-    def _select_language(self, store, language):
-        itr = store.get_iter_first()
-        while itr and language not in localization.expand_langs(store[itr][COL_LANG_SETTING]):
-            itr = store.iter_next(itr)
-
-        # If we were provided with an unsupported language, just use the default.
-        if not itr:
-            return
-
-        treeview = self.builder.get_object("langsupportView")
-        selection = treeview.get_selection()
-        selection.select_iter(itr)
-        path = store.get_path(itr)
-        treeview.scroll_to_cell(path)
+    def _add_language(self, store, native, english, lang):
+        native_span = '<span lang="%s">%s</span>' % (lang, native)
+        store.append([native_span, english, lang])
+
+    def _addLocale(self, store, native, locale):
+        native_span = '<span lang="%s">%s</span>' % (re.sub(r'\..*', '', locale), native)
+
+        # native, locale, selected, additional
+        store.append([native_span, locale, locale in self._selected_locales,
+                      locale != self.data.lang.lang])
+
+    def _refresh_locale_store(self, lang):
+        """Refresh the localeStore with locales for the given language."""
+
+        self._localeStore.clear()
+        locales = localization.get_language_locales(lang)
+        for locale in locales:
+            self._addLocale(self._localeStore,
+                            localization.get_native_name(locale),
+                            locale)
+
+        # select the first locale (with the highest rank)
+        set_treeview_selection(self._localeView, locales[0], col=1)
+
+    def _select_locale(self, locale):
+        """
+        Try to select the given locale in the language and locale
+        treeviews. This method tries to find the best match for the given
+        locale.
+
+        :return: a pair of selected iterators (language and locale)
+        :rtype: a pair of GtkTreeIter or None objects
+
+        """
+
+        # get lang and select it
+        parts = localization.parse_langcode(locale)
+        if "language" not in parts:
+            # invalid locale, cannot select
+            return (None, None)
+
+        lang_itr = set_treeview_selection(self._langView, parts["language"], col=2)
+        self._refresh_locale_store(parts["language"]) #XXX: needed?
+
+        # find matches and use the one with the highest rank
+        locales = localization.get_language_locales(locale)
+        locale_itr = set_treeview_selection(self._localeView, locales[0], col=1)
+
+        return (lang_itr, locale_itr)
 
     def _matches_entry(self, model, itr, *args):
         # Need to strip out the pango markup before attempting to match.
         # Otherwise, starting to type "span" for "spanish" will match everything
         # due to the enclosing span tag.
         (success, attrs, native, accel) = Pango.parse_markup(model[itr][COL_NATIVE_NAME], -1, "_")
-        english = model[itr][COL_ENGLISH_NAME]
-        entry = self._langsupportEntry.get_text().strip()
+        english = model[itr][1]
+        entry = self._languageEntry.get_text().strip()
 
         # Nothing in the text entry?  Display everything.
         if not entry:
@@ -179,21 +191,52 @@ class LangsupportSpoke(NormalSpoke):
         else:
             return False
 
-    def _mark_selected_bold(self, column, renderer, model, itr, idx):
-        value = model[itr][idx]
-        if model[itr][COL_SELECTED]:
-            value = "<b>%s</b>" % value
-        renderer.set_property("markup", value)
+    def _render_lang_selected(self, column, renderer, model, itr, user_data=None):
+        (lang_store, sel_itr) = self._langSelection.get_selected()
+
+        lang_locales = set(localization.get_language_locales(model[itr][2]))
+        if sel_itr and lang_store[sel_itr][2] == model[itr][2]:
+            renderer.set_property("pixbuf", self._right_arrow.get_pixbuf())
+        else:
+            renderer.set_property("pixbuf", None)
+
+    def _mark_selected_locale_bold(self, column, renderer, model, itr, user_data=None):
+        if model[itr][2]:
+            renderer.set_property("weight", Pango.Weight.BOLD.real)
+        else:
+            renderer.set_property("weight", Pango.Weight.NORMAL.real)
 
-    def on_langsupport_toggled(self, renderer, path):
-        selected = not self._langsupportStoreFilter[path][COL_SELECTED]
-        itr = self._langsupportStoreFilter.get_iter(path)
-        itr = self._langsupportStoreFilter.convert_iter_to_child_iter(itr)
-        self._langsupportStore[itr][COL_SELECTED] = selected
+    def _mark_selected_language_bold(self, column, renderer, model, itr, user_data=None):
+        lang_locales = set(localization.get_language_locales(model[itr][2]))
+        if not lang_locales.isdisjoint(self._selected_locales):
+            renderer.set_property("weight", Pango.Weight.BOLD.real)
+        else:
+            renderer.set_property("weight", Pango.Weight.NORMAL.real)
+
+    # Signal handlers.
+    def on_lang_selection_changed(self, selection):
+        (store, selected) = selection.get_selected_rows()
+
+        if selected:
+            lang = store[selected[0]][2]
+            self._refresh_locale_store(lang)
+        else:
+            self._localeStore.clear()
+
+    def on_locale_toggled(self, renderer, path):
+        itr = self._localeStore.get_iter(path)
+        row = self._localeStore[itr]
+
+        row[2] = not row[2]
+
+        if row[2]:
+            self._selected_locales.add(row[1])
+        else:
+            self._selected_locales.remove(row[1])
 
     def on_clear_icon_clicked(self, entry, icon_pos, event):
         if icon_pos == Gtk.EntryIconPosition.SECONDARY:
             entry.set_text("")
 
     def on_entry_changed(self, *args):
-        self._langsupportStoreFilter.refilter()
+        self._languageStoreFilter.refilter()
-- 
1.7.11.7



More information about the anaconda-patches mailing list