[PATCH 07/14] Rework the Welcome spoke to allow users choose from all locales

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


What we actually need from users is a locale, value that can be set in the $LANG
environment variable. However, what we get from the files with translations are
usually languages (en, cs, es, de, ...). Instead of picking up one locale for
each such language, we may offer user all locales for all languages we have
translations for and let them choose the exact locale they want to use. By
setting it as the value of the $LANG variable, we then get the right
translations.

Resolves: rhbz#1006458
(cherry-picked 44f9c89ef467c3acd4d53dbb84a8c275cbcbe951 from master)

Signed-off-by: Vratislav Podzimek <vpodzime at redhat.com>
---
 pyanaconda/localization.py             |  16 +-
 pyanaconda/ui/gui/spokes/welcome.glade | 336 ++++++++++++++++-----------------
 pyanaconda/ui/gui/spokes/welcome.py    | 224 ++++++++++++----------
 pyanaconda/ui/gui/utils.py             |  40 ++++
 4 files changed, 337 insertions(+), 279 deletions(-)

diff --git a/pyanaconda/localization.py b/pyanaconda/localization.py
index 8218ea9..349b47d 100644
--- a/pyanaconda/localization.py
+++ b/pyanaconda/localization.py
@@ -216,34 +216,34 @@ def get_native_name(locale):
 def get_available_translations(localedir=None):
     """
     Method that generates (i.e. returns a generator) available translations for
-    the given domain and localedir.
+    the installer in the given localedir.
 
     :type localedir: str
-    :return: generator yielding available translations
+    :return: generator yielding available translations (languages)
     :rtype: generator yielding strings
 
     """
 
     localedir = localedir or gettext._default_localedir
 
-    messagefiles = sorted(glob.glob(localedir + "/*/LC_MESSAGES/anaconda.mo"))
+    # usually there are no message files for en
+    messagefiles = sorted(glob.glob(localedir + "/*/LC_MESSAGES/anaconda.mo") +
+                          ["blob/en/blob/blob"])
     trans_gen = (path.split(os.path.sep)[-3] for path in messagefiles)
 
-    # usually there are no message files for en
-    langs = {"en"}
-    yield "en_US.UTF-8"
+    langs = set()
 
     for trans in trans_gen:
         parts = parse_langcode(trans)
         lang = parts.get("language", "")
         if lang and lang not in langs:
             langs.add(lang)
+            # check if there are any locales for the language
             locales = get_language_locales(lang)
             if not locales:
                 continue
 
-            # take the first locale (with highest rank) for the language
-            yield locales[0]
+            yield lang
 
 def get_language_locales(lang):
     """
diff --git a/pyanaconda/ui/gui/spokes/welcome.glade b/pyanaconda/ui/gui/spokes/welcome.glade
index 1f44cab..915bf5f 100644
--- a/pyanaconda/ui/gui/spokes/welcome.glade
+++ b/pyanaconda/ui/gui/spokes/welcome.glade
@@ -126,6 +126,29 @@
       <action-widget response="1">uhdContinueButton</action-widget>
     </action-widgets>
   </object>
+  <object class="GtkListStore" id="languageStore">
+    <columns>
+      <!-- column-name nativeName -->
+      <column type="gchararray"/>
+      <!-- column-name englishName -->
+      <column type="gchararray"/>
+      <!-- column-name langSetting -->
+      <column type="gchararray"/>
+      <!-- column-name separator -->
+      <column type="gboolean"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="localeStore">
+    <columns>
+      <!-- column-name nativeName -->
+      <column type="gchararray"/>
+      <!-- column-name locale -->
+      <column type="gchararray"/>
+    </columns>
+  </object>
+  <object class="GtkTreeModelFilter" id="languageStoreFilter">
+    <property name="child_model">languageStore</property>
+  </object>
   <object class="GtkDialog" id="betaWarnDialog">
     <property name="can_focus">False</property>
     <property name="border_width">5</property>
@@ -254,24 +277,29 @@ If you do not understand or accept the risks, then please exit this program by c
       <action-widget response="1">continueButton</action-widget>
     </action-widgets>
   </object>
-  <object class="AnacondaSpokeWindow" id="languageSpokeWindow">
+  <object class="AnacondaStandaloneWindow" id="welcomeWindow">
     <property name="startup_id">filler</property>
     <property name="can_focus">False</property>
     <property name="startup_id">filler</property>
-    <signal name="button-clicked" handler="on_back_clicked" swapped="no"/>
+    <property name="mnemonics_visible">False</property>
+    <property name="focus_visible">False</property>
+    <property name="window_name"></property>
     <child internal-child="main_box">
-      <object class="GtkBox" id="AnacondaSpokeWindow-main_box1">
+      <object class="GtkBox" id="AnacondaStandaloneWindow-main_box1">
         <property name="can_focus">False</property>
         <property name="orientation">vertical</property>
-        <property name="spacing">6</property>
         <child internal-child="nav_box">
-          <object class="GtkEventBox" id="AnacondaSpokeWindow-nav_box1">
+          <object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1">
+            <property name="can_focus">False</property>
             <child internal-child="nav_area">
-              <object class="GtkGrid" id="AnacondaSpokeWindow-nav_area1">
+              <object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1">
                 <property name="can_focus">False</property>
-                <property name="margin_left">6</property>
-                <property name="margin_right">6</property>
-                <property name="margin_top">6</property>
+                <child>
+                  <placeholder/>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
               </object>
             </child>
           </object>
@@ -282,88 +310,11 @@ If you do not understand or accept the risks, then please exit this program by c
           </packing>
         </child>
         <child internal-child="alignment">
-          <object class="GtkAlignment" id="AnacondaSpokeWindow-alignment1">
-            <property name="can_focus">False</property>
-            <property name="yalign">0</property>
-            <property name="xscale">1</property>
-            <property name="yscale">1</property>
-            <property name="bottom_padding">48</property>
-            <property name="left_padding">24</property>
-            <property name="right_padding">24</property>
-            <child internal-child="action_area">
-              <object class="GtkBox" id="languageSpokeWindowContentBox">
-                <property name="can_focus">False</property>
-                <property name="orientation">vertical</property>
-                <property name="spacing">6</property>
-                <child>
-                  <placeholder/>
-                </child>
-              </object>
-            </child>
-          </object>
-          <packing>
-            <property name="expand">True</property>
-            <property name="fill">True</property>
-            <property name="position">1</property>
-          </packing>
-        </child>
-        <child>
-          <placeholder/>
-        </child>
-      </object>
-    </child>
-  </object>
-  <object class="GtkListStore" id="languageStore">
-    <columns>
-      <!-- column-name nativeName -->
-      <column type="gchararray"/>
-      <!-- column-name englishName -->
-      <column type="gchararray"/>
-      <!-- column-name langSetting -->
-      <column type="gchararray"/>
-      <!-- column-name separator -->
-      <column type="gboolean"/>
-    </columns>
-  </object>
-  <object class="GtkTreeModelFilter" id="languageStoreFilter">
-    <property name="child_model">languageStore</property>
-  </object>
-  <object class="AnacondaStandaloneWindow" id="welcomeWindow">
-    <property name="startup_id">filler</property>
-    <property name="can_focus">False</property>
-    <property name="startup_id">filler</property>
-    <property name="mnemonics_visible">False</property>
-    <property name="focus_visible">False</property>
-    <property name="window_name"/>
-    <child internal-child="main_box">
-      <object class="GtkBox" id="AnacondaStandaloneWindow-main_box1">
-        <property name="can_focus">False</property>
-        <property name="orientation">vertical</property>
-        <child internal-child="nav_box">
-           <object class="GtkEventBox" id="AnacondaStandaloneWindow-nav_box1">
-              <child internal-child="nav_area">
-                <object class="GtkGrid" id="AnacondaStandaloneWindow-nav_area1">
-                  <property name="can_focus">False</property>
-                  <child>
-                    <placeholder/>
-                  </child>
-                </object>
-              </child>
-           </object>
-           <packing>
-             <property name="expand">False</property>
-             <property name="fill">False</property>
-             <property name="position">0</property>
-           </packing>
-        </child>
-        <child internal-child="alignment">
           <object class="GtkAlignment" id="AnacondaStandaloneWindow-alignment1">
             <property name="can_focus">False</property>
             <property name="hexpand">True</property>
             <property name="vexpand">True</property>
             <property name="yalign">0</property>
-            <property name="xscale">1</property>
-            <property name="yscale">1</property>
             <property name="bottom_padding">48</property>
             <property name="left_padding">24</property>
             <property name="right_padding">24</property>
@@ -391,7 +342,7 @@ If you do not understand or accept the risks, then please exit this program by c
                 </child>
                 <child>
                   <object class="GtkLabel" id="pickLanguageLabel">
-                   <property name="height_request">45</property>
+                    <property name="height_request">45</property>
                     <property name="visible">True</property>
                     <property name="can_focus">False</property>
                     <property name="valign">start</property>
@@ -408,128 +359,167 @@ If you do not understand or accept the risks, then please exit this program by c
                   </packing>
                 </child>
                 <child>
-                  <object class="GtkAlignment" id="languageAlignment">
+                  <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="row_spacing">6</property>
+                    <property name="column_spacing">6</property>
                     <child>
-                      <object class="GtkBox" id="box1">
+                      <object class="GtkScrolledWindow" id="languageWindow">
                         <property name="visible">True</property>
-                        <property name="can_focus">False</property>
+                        <property name="can_focus">True</property>
                         <property name="hexpand">True</property>
                         <property name="vexpand">True</property>
-                        <property name="orientation">vertical</property>
+                        <property name="hscrollbar_policy">never</property>
+                        <property name="shadow_type">in</property>
                         <child>
-                          <object class="GtkScrolledWindow" id="languageWindow">
+                          <object class="GtkTreeView" id="languageView">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
-                            <property name="has_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>
+                            <property name="model">languageStoreFilter</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="languageViewSelection">
+                                <signal name="changed" handler="on_lang_selection_changed" swapped="no"/>
+                              </object>
+                            </child>
                             <child>
-                              <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">languageStoreFilter</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="languageViewSelection">
-                                    <signal name="changed" handler="on_selection_changed" swapped="no"/>
+                              <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>
+                                    <attribute name="markup">0</attribute>
+                                  </attributes>
                                 </child>
+                              </object>
+                            </child>
+                            <child>
+                              <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="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="font">Cantarell 12</property>
-                                      </object>
-                                      <attributes>
-                                        <attribute name="markup">0</attribute>
-                                      </attributes>
-                                    </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="text">1</attribute>
+                                  </attributes>
                                 </child>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="langSelectedColumn">
+                                <property name="title" translatable="yes">selected</property>
                                 <child>
-                                  <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="font">Cantarell Italic 14</property>
-                                        <property name="foreground">gray</property>
-                                      </object>
-                                      <attributes>
-                                        <attribute name="text">1</attribute>
-                                      </attributes>
-                                    </child>
-                                  </object>
+                                  <object class="GtkCellRendererPixbuf" id="langSelectedRenderer"/>
                                 </child>
                               </object>
                             </child>
                           </object>
-                          <packing>
-                            <property name="expand">True</property>
-                            <property name="fill">True</property>
-                            <property name="position">0</property>
-                          </packing>
                         </child>
+                      </object>
+                      <packing>
+                        <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="languageEntry">
+                        <property name="visible">True</property>
+                        <property name="can_focus">True</property>
+                        <property name="valign">start</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">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="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="GtkEntry" id="languageEntry">
+                          <object class="GtkTreeView" id="localeView">
                             <property name="visible">True</property>
                             <property name="can_focus">True</property>
-                            <property name="valign">start</property>
-                            <property name="margin_top">6</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>
-                            <signal name="changed" handler="on_entry_changed" swapped="no"/>
-                            <signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/>
+                            <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">
+                                <signal name="changed" handler="on_locale_selection_changed" swapped="no"/>
+                              </object>
+                            </child>
+                            <child>
+                              <object class="GtkTreeViewColumn" id="nativeName1">
+                                <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="nativeNameRenderer1">
+                                    <property name="font">Cantarell 12</property>
+                                  </object>
+                                  <attributes>
+                                    <attribute name="markup">0</attribute>
+                                  </attributes>
+                                </child>
+                              </object>
+                            </child>
                           </object>
-
-                          <packing>
-                            <property name="expand">False</property>
-                            <property name="fill">True</property>
-                            <property name="position">1</property>
-                          </packing>
                         </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="fill">True</property>
-                    <property name="position">2</property>
-                  </packing>
-                </child>
-                <child>
-                  <object class="GtkCheckButton" id="setKeyboardCheckButton">
-                    <property name="label" translatable="yes">Set _keyboard to default layout for selected language.</property>
-                    <property name="visible">True</property>
-                    <property name="can_focus">True</property>
-                    <property name="receives_default">False</property>
-                    <property name="use_underline">True</property>
-                    <property name="xalign">0</property>
-                    <property name="draw_indicator">True</property>
                   </object>
                   <packing>
                     <property name="expand">False</property>
                     <property name="fill">True</property>
-                    <property name="pack_type">end</property>
-                    <property name="position">3</property>
+                    <property name="position">2</property>
                   </packing>
                 </child>
               </object>
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py
index 5d982a4..f5b429e 100644
--- a/pyanaconda/ui/gui/spokes/welcome.py
+++ b/pyanaconda/ui/gui/spokes/welcome.py
@@ -17,25 +17,26 @@
 # Red Hat, Inc.
 #
 # Red Hat Author(s): Chris Lumens <clumens at redhat.com>
+#                    Vratislav Podzimek <vpodzime at redhat.com>
 #
 
 import sys
 import re
+import langtable
 
-# pylint: disable-msg=E0611
-from gi.repository import AnacondaWidgets, Gtk, Pango
+from gi.repository import Gtk, Pango
 from pyanaconda.ui.gui.hubs.summary import SummaryHub
 from pyanaconda.ui.gui.spokes import StandaloneSpoke
-from pyanaconda.ui.gui.utils import enlightbox
+from pyanaconda.ui.gui.utils import enlightbox, set_treeview_selection
 
 from pyanaconda import localization
 from pyanaconda.product import distributionText, isFinal, productName, productVersion
 from pyanaconda import keyboard
-from pyanaconda import timezone
 from pyanaconda import flags
 from pyanaconda import geoloc
-from pyanaconda.i18n import _, N_
+from pyanaconda.i18n import _
 from pyanaconda.iutil import is_unsupported_hw
+from pyanaconda.constants import DEFAULT_LANG
 
 import logging
 log = logging.getLogger("anaconda")
@@ -45,7 +46,8 @@ __all__ = ["WelcomeLanguageSpoke"]
 class WelcomeLanguageSpoke(StandaloneSpoke):
     mainWidgetName = "welcomeWindow"
     uiFile = "spokes/welcome.glade"
-    builderObjects = ["languageStore", "languageStoreFilter", "welcomeWindow", "betaWarnDialog", "unsupportedHardwareDialog"]
+    builderObjects = ["languageStore", "languageStoreFilter", "localeStore",
+                      "welcomeWindow", "betaWarnDialog", "unsupportedHardwareDialog"]
 
     preForHub = SummaryHub
     priority = 0
@@ -56,10 +58,9 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         self._origStrings = {}
 
     def apply(self):
-        selected = self.builder.get_object("languageViewSelection")
-        (store, itr) = selected.get_selected()
+        (store, itr) = self._localeSelection.get_selected()
 
-        locale = store[itr][2]
+        locale = store[itr][1]
         localization.setup_locale(locale, self.data.lang)
 
         # Skip timezone and keyboard default setting for kickstart installs.
@@ -79,14 +80,18 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
             # current language
             self.data.timezone.timezone = loc_timezones[0]
 
-        lang_country = localization.get_locale_territory(self.data.lang.lang)
-        self._set_keyboard_defaults(store[itr][1], lang_country)
+        self._set_keyboard_defaults(self.data.lang.lang)
 
-    def _set_keyboard_defaults(self, lang_name, country):
+    def _set_keyboard_defaults(self, locale):
         """
         Set default keyboard settings (layouts, layout switching).
 
-        :param lang_name: name of the selected language (e.g. "Czech")
+        :param locale: locale string (see localization.LANGCODE_RE)
+        :type locale: str
+        :return: list of preferred keyboard layouts
+        :rtype: list of strings
+        :raise InvalidLocaleSpec: if an invalid locale is given (see
+                                  localization.LANGCODE_RE)
 
         """
 
@@ -102,23 +107,17 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
             #do not add layouts if there are any specified in the kickstart
             return
 
-        #get language name without any additional specifications
-        #e.g. 'English (United States)' -> 'English'
-        lang_name = lang_name.split()[0]
-
-        default_layout = self._xklwrapper.get_default_lang_country_layout(lang_name,
-                                                                          country)
-        if default_layout:
-            new_layouts = [default_layout]
+        layouts = localization.get_locale_keyboards(locale)
+        if layouts:
+            # take the first locale (with highest rank) from the list
+            new_layouts = [layouts[0]]
+            if not langtable.supports_ascii(layouts[0]):
+                # does not support typing ASCII chars, append the 'us' layout
+                new_layouts.append("us")
         else:
+            log.error("Failed to get layout for chosen locale '%s'" % locale)
             new_layouts = ["us"]
 
-        checkbutton = self.builder.get_object("setKeyboardCheckButton")
-        if not checkbutton.get_active() and "us" not in new_layouts:
-            #user doesn't want only the language-default layout, prepend
-            #'English (US)' layout
-            new_layouts.insert(0, "us")
-
         self.data.keyboard.x_layouts = new_layouts
         if flags.can_touch_runtime_system("replace runtime X layouts"):
             self._xklwrapper.replace_layouts(new_layouts)
@@ -129,6 +128,9 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
 
             if flags.can_touch_runtime_system("init layout switching"):
                 self._xklwrapper.set_switching_options(["grp:alt_shift_toggle"])
+                # activate the first (language-default) layout instead of the
+                # 'us' one
+                self._xklwrapper.activate_default_layout()
 
     @property
     def completed(self):
@@ -140,15 +142,33 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
     def _row_is_separator(self, model, itr, *args):
         return model[itr][3]
 
+    def _render_lang_selected(self, column, renderer, model, itr, user_data=None):
+        (lang_store, sel_itr) = self._langSelection.get_selected()
+
+        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 initialize(self):
-        store = self.builder.get_object("languageStore")
+        self._languageStore = self.builder.get_object("languageStore")
         self._languageStoreFilter = self.builder.get_object("languageStoreFilter")
         self._languageEntry = self.builder.get_object("languageEntry")
-        self._selection = self.builder.get_object("languageViewSelection")
-        self._view = self.builder.get_object("languageView")
+        self._langSelection = self.builder.get_object("languageViewSelection")
+        self._langSelectedRenderer = self.builder.get_object("langSelectedRenderer")
+        self._langSelectedColumn = self.builder.get_object("langSelectedColumn");
+        self._langView = self.builder.get_object("languageView")
+        self._localeView = self.builder.get_object("localeView")
+        self._localeStore = self.builder.get_object("localeStore")
+        self._localeSelection = self.builder.get_object("localeViewSelection")
 
         # We need to tell the view whether something is a separator or not.
-        self._view.set_row_separator_func(self._row_is_separator, None)
+        self._langView.set_row_separator_func(self._row_is_separator, None)
+
+        # 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)
 
         # We can use the territory from geolocation here
         # to preselect the translation, when it's available.
@@ -161,22 +181,28 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
             localization.setup_locale(locales[0], self.data.lang)
 
         # fill the list with available translations
-        for locale in localization.get_available_translations():
-            self._addLanguage(store, localization.get_native_name(locale),
-                              localization.get_english_name(locale), locale)
+        for lang in localization.get_available_translations():
+            self._addLanguage(self._languageStore,
+                              localization.get_native_name(lang),
+                              localization.get_english_name(lang), lang)
 
         # Move the default language (whatever was provided on the command line,
         # or by kickstart, or by geoip, or English if nothing else) to the top
         # of the list and select it by default.  People find it confusing to be
         # dropped into the middle of a scrollable list.
-        (store, itr) = self._selection.get_selected()
-        if not itr:
-            itr = self._selectLanguage(self.data.lang.lang)
+        lang_itr, locale_itr = self._select_locale(self.data.lang.lang)
+
+        if not lang_itr or not locale_itr:
+            log.error("Failed to select language %s, using the default %s",
+                      self.data.lang.lang, DEFAULT_LANG)
+            lang_itr, locale_itr = self._select_locale(DEFAULT_LANG)
+            self.data.lang.lang = DEFAULT_LANG
 
-        # store is the filtered store, and itr is an iter on it.  We need to
+        filter_store = self._languageStoreFilter
+        # filtered store and lang_itr is an iter on it.  We need to
         # convert to an iter on the underlying store.
-        itr = store.convert_iter_to_child_iter(itr)
-        store = store.get_model()
+        itr = filter_store.convert_iter_to_child_iter(lang_itr)
+        store = filter_store.get_model()
         store.move_after(itr, None)
 
         # And then we add a separator after the default chosen language.
@@ -200,7 +226,7 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         # Change the translations on labels and buttons that do not have
         # substitution text.
         for name in ["pickLanguageLabel", "betaWarnTitle", "betaWarnDesc",
-                     "quitButton", "continueButton", "setKeyboardCheckButton"]:
+                     "quitButton", "continueButton"]:
             self._retranslate_one(name)
 
         # The welcome label is special - it has text that needs to be
@@ -211,7 +237,7 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
             self._origStrings[welcomeLabel] = welcomeLabel.get_label()
 
         before = self._origStrings[welcomeLabel]
-        xlated = _(before) % (productName.upper(), productVersion)
+        xlated = _(before) % {"name" : productName.upper(), "version" : productVersion}
         welcomeLabel.set_label(xlated)
 
         # And of course, don't forget the underlying window.
@@ -219,28 +245,17 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         self.window.retranslate(lang)
 
     def refresh(self):
-        self._selectLanguage(self.data.lang.lang)
-
-        # Rip the label and language selection window
-        # from where it is right now and add it to this
-        # spoke.
-        # This way we can share the dialog and code
-        # between Welcome and Language spokes
-        langLabel = self.builder.get_object("pickLanguageLabel")
-        langLabel.get_parent().remove(langLabel)
-        langAlign = self.builder.get_object("languageAlignment")
-        langAlign.get_parent().remove(langAlign)
-
-        content = self.builder.get_object("welcomeWindowContentBox")
-        content.pack_start(child = langLabel, fill = True, expand = False, padding = 0)
-        content.pack_start(child = langAlign, fill = True, expand = True, padding = 0)
-
+        self._select_locale(self.data.lang.lang)
         self._languageEntry.set_text("")
         self._languageStoreFilter.refilter()
 
-    def _addLanguage(self, store, native, english, setting):
-        lang = '<span lang="%s">%s</span>' % (re.sub(r'\..*', '', setting), native)
-        store.append([lang, english, setting, False])
+    def _addLanguage(self, store, native, english, lang):
+        native_span = '<span lang="%s">%s</span>' % (lang, native)
+        store.append([native_span, english, lang, False])
+
+    def _addLocale(self, store, native, locale):
+        native_span = '<span lang="%s">%s</span>' % (re.sub(r'\..*', '', locale), native)
+        store.append([native_span, locale])
 
     def _matchesEntry(self, model, itr, *args):
         # Need to strip out the pango markup before attempting to match.
@@ -262,51 +277,64 @@ class WelcomeLanguageSpoke(StandaloneSpoke):
         else:
             return False
 
-    def _selectLanguage(self, language):
-        treeview = self.builder.get_object("languageView")
-        selection = treeview.get_selection()
-        store = treeview.get_model()
-        itr = store.get_iter_first()
-        # store[itr][3] is True if this row is a separator in the view, so we
-        # want to skip those.
-        while itr and not store[itr][3] \
-                and language not in localization.expand_langs(store[itr][2]):
-            itr = store.iter_next(itr)
-
-        # If we were provided with an unsupported language, just use the default.
-        if not itr:
-            return
+    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
+
+        """
 
-        selection.select_iter(itr)
-        path = store.get_path(itr)
-        # row_align=0.5 tells GTK to move the cell to the middle of the
-        # treeview viewport (0.0 should align it with the top, 1.0 with bottom)
-        # If the cell is the uppermost one, it should align it with the top
-        # of the viewport.
-        #
-        # Unfortunately, this does not work as expected due to a bug in GTK.
-        # So currently if the cell is the upper most one, it will sort of
-        # align it with the top of the viewport, leaving about 30% of
-        # it hidden. If the target cell is not the upper most one,
-        # it seems to just scroll the treeview a bit, leaving the
-        # target cell well out of the viewport.
-        #
-        # In short, once that GTK bug is fixed, row_align=0.5 should work
-        # correctly for showing both upper most cells and all other cells
-        # in the tree view.
-        treeview.scroll_to_cell(path, use_align=True, row_align=0.5)
-
-        return itr
+        # 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)
+
+        # 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 _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)
 
     # Signal handlers.
-    def on_selection_changed(self, selection):
+    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:
+            if hasattr(self.window, "set_may_continue"):
+                self.window.set_may_continue(False)
+            self._localeStore.clear()
+
+    def on_locale_selection_changed(self, selection):
         (store, selected) = selection.get_selected_rows()
         if hasattr(self.window, "set_may_continue"):
             self.window.set_may_continue(len(selected) > 0)
 
         if selected:
-            lang = store[selected[0]][2]
-            localization.setup_locale(lang, self.data.lang)
+            lang = store[selected[0]][1]
+            localization.setup_locale(lang)
             self.retranslate(lang)
 
     def on_clear_icon_clicked(self, entry, icon_pos, event):
diff --git a/pyanaconda/ui/gui/utils.py b/pyanaconda/ui/gui/utils.py
index 4ea71be..4456ab1 100644
--- a/pyanaconda/ui/gui/utils.py
+++ b/pyanaconda/ui/gui/utils.py
@@ -153,3 +153,43 @@ def really_show(widget):
     """
     widget.set_no_show_all(False)
     widget.show()
+
+def set_treeview_selection(treeview, item, col=0):
+    """
+    Select the given item in the given treeview and scroll to it.
+
+    :param treeview: treeview to select and item in
+    :type treeview: GtkTreeView
+    :param item: item to be selected
+    :type item: str
+    :param col: column to search for the item in
+    :type col: int
+    :return: selected iterator or None if item was not found
+    :rtype: GtkTreeIter or None
+
+    """
+
+    model = treeview.get_model()
+    itr = model.get_iter_first()
+    while itr and not model[itr][col] == item:
+        itr = model.iter_next(itr)
+
+    if not itr:
+        # item not found, cannot be selected
+        return None
+
+    # otherwise select the item and scroll to it
+    selection = treeview.get_selection()
+    selection.select_iter(itr)
+    path = model.get_path(itr)
+
+    # row_align=0.5 tells GTK to move the cell to the middle of the
+    # treeview viewport (0.0 should align it with the top, 1.0 with bottom)
+    # If the cell is the uppermost one, it should align it with the top
+    # of the viewport.
+    #
+    # Unfortunately, this does not work as expected due to a bug in GTK.
+    # (see rhbz#970048)
+    treeview.scroll_to_cell(path, use_align=True, row_align=0.5)
+
+    return itr
-- 
1.7.11.7



More information about the anaconda-patches mailing list