Kickstart command lang is extended with --addsupport option for specifying additional (to ksdata.lang.lang) language support to be installed (ksdata.lang.addsupport).
In the spoke GUI, ksdata.lang.lang (system lang) is selected and grayed-out (insensitive).
Selected (using checkbox) languages are displayed in bold, the list is searchable in the same way as the welcome spoke list. Currently only available translations for installer (the same as in welcome spoke) are in the list.
screencast: http://rvykydal.fedorapeople.org/langsupport.ogg
--- pyanaconda/install.py | 2 +- pyanaconda/kickstart.py | 4 ++-- pyanaconda/packaging/yumpayload.py | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 08b3966..cbaefce 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -128,7 +128,7 @@ def doInstall(storage, payload, ksdata, instClass): # system is bootable and configurable, and some other packages in order # to finish setting up the system. packages = storage.packages + ["authconfig", "firewalld"] - payload.preInstall(packages=packages, groups=payload.languageGroups(ksdata.lang.lang)) + payload.preInstall(packages=packages, groups=payload.languageGroups()) payload.install()
if flags.flags.livecdInstall: diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 9060c58..bcfcea6 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -576,9 +576,9 @@ class IscsiName(commands.iscsiname.FC6_IscsiName): blivet.iscsi.iscsi().initiator = self.iscsiname return retval
-class Lang(commands.lang.FC3_Lang): +class Lang(commands.lang.F19_Lang): def __init__(self, *args, **kwargs): - commands.lang.FC3_Lang.__init__(self, *args, **kwargs) + commands.lang.F19_Lang.__init__(self, *args, **kwargs) if not self.lang and not flags.automatedInstall: self.lang = DEFAULT_LANG
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index 4fc6177..02217ee 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -235,6 +235,13 @@ class YumPayload(PackagePayload):
self.txID = None
+ def _writeLangpacksConfig(self): + langs = [self.data.lang.lang] + self.data.lang.addsupport + log.debug("configuring langpacks for %s" % langs) + with open("/etc/yum/pluginconf.d/langpacks.conf", "a") as f: + f.write("# Added by Anaconda\n") + f.write("langpack_locales = %s\n" % ", ".join(langs)) + def _writeYumConfig(self): """ Write out anaconda's main yum configuration file. """ buf = """ @@ -345,6 +352,7 @@ reposdir=%s
releasever = self._yum.conf.yumvar['releasever'] self._writeYumConfig() + self._writeLangpacksConfig() self._resetYum(root=ROOT_PATH, keep_cache=True) log.debug("setting releasever to previous value of %s" % releasever) self._yum.preconf.releasever = releasever @@ -1094,16 +1102,18 @@ reposdir=%s
return groups
- def languageGroups(self, lang): + def languageGroups(self): + langlist = [self.data.lang.lang] + self.data.lang.addsupport groups = [] yum_groups = self._yumGroups
if yum_groups: with _yum_lock: - langs = expand_langs(lang) - groups = map(lambda x: [g.groupid for g in - yum_groups.get_groups() if g.langonly == x], - langs) + for lang in langlist: + langs = expand_langs(lang) + groups.extend(map(lambda x: [g.groupid for g in + yum_groups.get_groups() if g.langonly == x], + langs))
# the map gives us a list of results, this set call reduces # it down to a unique set, then list() makes it back into a list.
On Mon, 2013-03-25 at 10:22 +0100, Radek Vykydal wrote:
pyanaconda/install.py | 2 +- pyanaconda/kickstart.py | 4 ++-- pyanaconda/packaging/yumpayload.py | 20 +++++++++++++++----- 3 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 08b3966..cbaefce 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -128,7 +128,7 @@ def doInstall(storage, payload, ksdata, instClass): # system is bootable and configurable, and some other packages in order # to finish setting up the system. packages = storage.packages + ["authconfig", "firewalld"]
- payload.preInstall(packages=packages, groups=payload.languageGroups(ksdata.lang.lang))
payload.preInstall(packages=packages, groups=payload.languageGroups()) payload.install()
if flags.flags.livecdInstall:
diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 9060c58..bcfcea6 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -576,9 +576,9 @@ class IscsiName(commands.iscsiname.FC6_IscsiName): blivet.iscsi.iscsi().initiator = self.iscsiname return retval
-class Lang(commands.lang.FC3_Lang): +class Lang(commands.lang.F19_Lang): def __init__(self, *args, **kwargs):
commands.lang.FC3_Lang.__init__(self, *args, **kwargs)
commands.lang.F19_Lang.__init__(self, *args, **kwargs) if not self.lang and not flags.automatedInstall: self.lang = DEFAULT_LANGdiff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index 4fc6177..02217ee 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -235,6 +235,13 @@ class YumPayload(PackagePayload):
self.txID = None
- def _writeLangpacksConfig(self):
langs = [self.data.lang.lang] + self.data.lang.addsupportlog.debug("configuring langpacks for %s" % langs)with open("/etc/yum/pluginconf.d/langpacks.conf", "a") as f:f.write("# Added by Anaconda\n")f.write("langpack_locales = %s\n" % ", ".join(langs))- def _writeYumConfig(self): """ Write out anaconda's main yum configuration file. """ buf = """
@@ -345,6 +352,7 @@ reposdir=%s
releasever = self._yum.conf.yumvar['releasever'] self._writeYumConfig()
self._writeLangpacksConfig() self._resetYum(root=ROOT_PATH, keep_cache=True) log.debug("setting releasever to previous value of %s" % releasever) self._yum.preconf.releasever = releasever@@ -1094,16 +1102,18 @@ reposdir=%s
return groups
- def languageGroups(self, lang):
def languageGroups(self):
langlist = [self.data.lang.lang] + self.data.lang.addsupport groups = [] yum_groups = self._yumGroups if yum_groups: with _yum_lock:
langs = expand_langs(lang)groups = map(lambda x: [g.groupid for g inyum_groups.get_groups() if g.langonly == x],langs)
for lang in langlist:langs = expand_langs(lang)groups.extend(map(lambda x: [g.groupid for g inyum_groups.get_groups() if g.langonly == x],langs))
Could we please make this a bit better readable? It was quite cryptic before and now it's even worse.
--- pyanaconda/install.py | 2 +- pyanaconda/kickstart.py | 4 ++-- pyanaconda/packaging/yumpayload.py | 35 +++++++++++++++++++++++------------ 3 files changed, 26 insertions(+), 15 deletions(-)
diff --git a/pyanaconda/install.py b/pyanaconda/install.py index 08b3966..cbaefce 100644 --- a/pyanaconda/install.py +++ b/pyanaconda/install.py @@ -128,7 +128,7 @@ def doInstall(storage, payload, ksdata, instClass): # system is bootable and configurable, and some other packages in order # to finish setting up the system. packages = storage.packages + ["authconfig", "firewalld"] - payload.preInstall(packages=packages, groups=payload.languageGroups(ksdata.lang.lang)) + payload.preInstall(packages=packages, groups=payload.languageGroups()) payload.install()
if flags.flags.livecdInstall: diff --git a/pyanaconda/kickstart.py b/pyanaconda/kickstart.py index 9060c58..bcfcea6 100644 --- a/pyanaconda/kickstart.py +++ b/pyanaconda/kickstart.py @@ -576,9 +576,9 @@ class IscsiName(commands.iscsiname.FC6_IscsiName): blivet.iscsi.iscsi().initiator = self.iscsiname return retval
-class Lang(commands.lang.FC3_Lang): +class Lang(commands.lang.F19_Lang): def __init__(self, *args, **kwargs): - commands.lang.FC3_Lang.__init__(self, *args, **kwargs) + commands.lang.F19_Lang.__init__(self, *args, **kwargs) if not self.lang and not flags.automatedInstall: self.lang = DEFAULT_LANG
diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index 4fc6177..5eeb416 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -235,6 +235,13 @@ class YumPayload(PackagePayload):
self.txID = None
+ def _writeLangpacksConfig(self): + langs = [self.data.lang.lang] + self.data.lang.addsupport + log.debug("configuring langpacks for %s" % langs) + with open("/etc/yum/pluginconf.d/langpacks.conf", "a") as f: + f.write("# Added by Anaconda\n") + f.write("langpack_locales = %s\n" % ", ".join(langs)) + def _writeYumConfig(self): """ Write out anaconda's main yum configuration file. """ buf = """ @@ -345,6 +352,7 @@ reposdir=%s
releasever = self._yum.conf.yumvar['releasever'] self._writeYumConfig() + self._writeLangpacksConfig() self._resetYum(root=ROOT_PATH, keep_cache=True) log.debug("setting releasever to previous value of %s" % releasever) self._yum.preconf.releasever = releasever @@ -1094,20 +1102,23 @@ reposdir=%s
return groups
- def languageGroups(self, lang): - groups = [] + def languageGroups(self): yum_groups = self._yumGroups + if not yum_groups: + return []
- if yum_groups: - with _yum_lock: - langs = expand_langs(lang) - groups = map(lambda x: [g.groupid for g in - yum_groups.get_groups() if g.langonly == x], - langs) - - # the map gives us a list of results, this set call reduces - # it down to a unique set, then list() makes it back into a list. - return list(set(itertools.chain(*groups))) + lang_codes = [self.data.lang.lang] + self.data.lang.addsupport + lang_groups = set() + + with _yum_lock: + groups = yum_groups.get_groups() + for lang_code in lang_codes: + for lang_code_guess in expand_langs(lang_code): + for group in groups: + if group.langonly == lang_code_guess: + lang_groups.add(group.groupid) + + return list(lang_groups)
def groupDescription(self, groupid): """ Return name/description tuple for the group specified by id. """
--- pyanaconda/ui/gui/spokes/langsupport.glade | 201 +++++++++++++++++++++++++++++ pyanaconda/ui/gui/spokes/langsupport.py | 195 ++++++++++++++++++++++++++++ 2 files changed, 396 insertions(+) create mode 100644 pyanaconda/ui/gui/spokes/langsupport.glade create mode 100644 pyanaconda/ui/gui/spokes/langsupport.py
diff --git a/pyanaconda/ui/gui/spokes/langsupport.glade b/pyanaconda/ui/gui/spokes/langsupport.glade new file mode 100644 index 0000000..b7ef1ce --- /dev/null +++ b/pyanaconda/ui/gui/spokes/langsupport.glade @@ -0,0 +1,201 @@ +<?xml version="1.0" encoding="UTF-8"?> +<interface> + <!-- interface-requires gtk+ 3.0 --> + <!-- interface-requires AnacondaWidgets 1.0 --> + <object class="GtkListStore" id="langsupportStore"> + <columns> + <!-- column-name nativeName --> + <column type="gchararray"/> + <!-- column-name englishName --> + <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> + <object class="AnacondaSpokeWindow" id="langsupportWindow"> + <property name="startup_id">filler</property> + <property name="can_focus">False</property> + <property name="startup_id">filler</property> + <property name="window_name" translatable="yes">LANGUAGE SUPPORT</property> + <signal name="button-clicked" handler="on_back_clicked" swapped="no"/> + <child internal-child="main_box"> + <object class="GtkBox" id="AnacondaSpokeWindow-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"> + <property name="app_paintable">True</property> + <property name="can_focus">False</property> + <child internal-child="nav_area"> + <object class="GtkGrid" id="AnacondaSpokeWindow-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> + </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="AnacondaSpokeWindow-alignment1"> + <property name="can_focus">False</property> + <property name="xalign">0</property> + <property name="yalign">0</property> + <property name="top_padding">12</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="AnacondaSpokeWindow-action_area1"> + <property name="can_focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <child> + <object class="GtkLabel" id="selectLabel"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="halign">start</property> + <property name="label" translatable="yes">Select additional language support to be installed</property> + <attributes> + <attribute name="font-desc" value="Cantarell 12"/> + <attribute name="weight" value="normal"/> + <attribute name="scale" value="1"/> + </attributes> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkBox" id="box1"> + <property name="visible">True</property> + <property name="can_focus">False</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="orientation">vertical</property> + <child> + <object class="GtkScrolledWindow" id="langsupportScrolledwindow1"> + <property name="visible">True</property> + <property name="can_focus">True</property> + <property name="hexpand">True</property> + <property name="vexpand">True</property> + <property name="shadow_type">in</property> + <child> + <object class="GtkTreeView" id="langsupportView"> + <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="headers_visible">False</property> + <property name="headers_clickable">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> + </child> + <child> + <object class="GtkTreeViewColumn" id="nativeNameCol"> + <property name="title" translatable="yes">column</property> + <property name="expand">True</property> + <child> + <object class="GtkCellRendererText" id="nativeNameRenderer"/> + <attributes> + <attribute name="markup">0</attribute> + </attributes> + </child> + </object> + </child> + <child> + <object class="GtkTreeViewColumn" id="englishNameCol"> + <property name="title" translatable="yes">column</property> + <property name="expand">True</property> + <child> + <object class="GtkCellRendererText" id="englishNameRenderer"/> + <attributes> + <attribute name="markup">1</attribute> + </attributes> + </child> + </object> + </child> + </object> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkEntry" id="langsupportEntry"> + <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> + <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> + </packing> + </child> + </object> + <packing> + <property name="expand">True</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </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> +</interface> diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py new file mode 100644 index 0000000..c586346 --- /dev/null +++ b/pyanaconda/ui/gui/spokes/langsupport.py @@ -0,0 +1,195 @@ +# Language support selection spoke classes +# +# Copyright (C) 2011-2012 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Radek Vykydal rvykydal@redhat.com +# + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) +N_ = lambda x: x + +from gi.repository import Gtk, Pango +from pyanaconda.flags import flags +from pyanaconda.ui.gui.spokes import NormalSpoke +from pyanaconda.ui.gui.categories.localization import LocalizationCategory +from pyanaconda.localization import Language, LOCALE_PREFERENCES, expand_langs + +import re + +import logging +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"] + mainWidgetName = "langsupportWindow" + uiFile = "spokes/langsupport.glade" + + category = LocalizationCategory + + icon = "accessories-character-map-symbolic" + title = N_("_LANGUAGE SUPPORT") + + def __init__(self, *args, **kwargs): + NormalSpoke.__init__(self, *args, **kwargs) + + 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)): + column = self.builder.get_object(col) + renderer = self.builder.get_object(rend) + column.set_cell_data_func(renderer, self._mark_selected_bold, idx) + + language = Language(LOCALE_PREFERENCES, territory=None) + # source of lang code <-> UI name mapping + self.locale_infos_for_data = language.translations + self.locale_infos_for_ui = language.translations + + for code, info in sorted(self.locale_infos_for_ui.items()): + self._add_language(self._langsupportStore, info.display_name, + info.english_name, info.short_name, + False, True) + + self._select_language(self._langsupportStore, self.data.lang.lang) + + 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]] + + def refresh(self): + lang_infos = self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_ui) + lang_short_names = [info.short_name for info in lang_infos] + if len(lang_short_names) > 1: + log.warning("Found multiple locales for lang %s: %s, picking first" % + (self.data.lang.lang, lang_short_names)) + # Just take the first found + # TODO - for corner cases choose the one that is common prefix + lang_short_names = lang_short_names[:1] + + addsupp_short_names = [] + for code in self.data.lang.addsupport: + code_infos = self._find_localeinfos_for_code(code, self.locale_infos_for_ui) + addsupp_short_names.extend(info.short_name for info in code_infos) + + for row in self._langsupportStore: + if row[COL_LANG_SETTING] in addsupp_short_names: + row[COL_SELECTED] = True + if row[COL_LANG_SETTING] in lang_short_names: + row[COL_SELECTED] = True + row[COL_IS_ADDITIONAL] = False + + @property + def showable(self): + return not flags.livecdInstall + + @property + def status(self): + # TODO: translate + infos = set(self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1]) + for code in self.data.lang.addsupport: + infos.update(self._find_localeinfos_for_code(code, self.locale_infos_for_data)) + return ", ".join(info.english_name for info in infos) + + @property + def mandatory(self): + return False + + @property + def completed(self): + return True + + def _find_localeinfos_for_code(self, code, infos): + try: + retval = [infos[code]] + except KeyError: + retval = [info for _code, info in infos.items() + if code in expand_langs(_code)] + log.debug("locale info 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('..*', '', setting), native), + english, setting, selected, additional]) + + def _select_language(self, store, language): + itr = store.get_iter_first() + while itr and language not in 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 _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() + + # Nothing in the text entry? Display everything. + if not entry: + return True + + # Otherwise, filter the list showing only what is matched by the + # text entry. Either the English or native names can match. + lowered = entry.lower() + if lowered in native.lower() or lowered in english.lower(): + return True + 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 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 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()
On Mon, 2013-03-25 at 10:22 +0100, Radek Vykydal wrote:
pyanaconda/ui/gui/spokes/langsupport.glade | 201 +++++++++++++++++++++++++++++ pyanaconda/ui/gui/spokes/langsupport.py | 195 ++++++++++++++++++++++++++++ 2 files changed, 396 insertions(+) create mode 100644 pyanaconda/ui/gui/spokes/langsupport.glade create mode 100644 pyanaconda/ui/gui/spokes/langsupport.py diff --git a/pyanaconda/ui/gui/spokes/langsupport.py b/pyanaconda/ui/gui/spokes/langsupport.py new file mode 100644 index 0000000..c586346 --- /dev/null +++ b/pyanaconda/ui/gui/spokes/langsupport.py @@ -0,0 +1,195 @@ +# Language support selection spoke classes +# +# Copyright (C) 2011-2012 Red Hat, Inc.
It's 2013, now ;)
pyanaconda/ui/gui/spokes/langsupport.glade | 201 +++++++++++++++++++++++++++++ pyanaconda/ui/gui/spokes/langsupport.py | 195 ++++++++++++++++++++++++++++ 2 files changed, 396 insertions(+) create mode 100644 pyanaconda/ui/gui/spokes/langsupport.glade create mode 100644 pyanaconda/ui/gui/spokes/langsupport.py
Remember to add your new files to po/POTFILES.in or they won't get translated.
<child><object class="GtkEntry" id="langsupportEntry"><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><signal name="changed" handler="on_entry_changed" swapped="no"/><signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/></object>
One thing we started doing on these kinds of widgets on the UI throwdown day last week was add secondary text to indicate this is a search box. Check out 9b632169e5b83d57d627d39f94f4315abbb9aabf for an example. I think we should do the same thing here, too.
- def apply(self):
self.data.lang.addsupport = [row[COL_LANG_SETTING]for row in self._langsupportStoreif row[COL_SELECTED] and row[COL_IS_ADDITIONAL]]
I was kind of imagining that this would work by adding *-support packages to the list of packages to be installed. I suppose at this point we don't necessarily have packaging set up, though, do we? Passsing through kickstart elsewhere like this is probably for the best, though.
You may not remember this, but a long time ago we even had a langsupport kickstart command. I wonder if it'd be better to bring that back than to add additional options to existing commands. Which way is more confusing?
- @property
- def status(self):
# TODO: translateinfos = set(self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1])for code in self.data.lang.addsupport:infos.update(self._find_localeinfos_for_code(code, self.locale_infos_for_data))return ", ".join(info.english_name for info in infos)
What happens if I select a lot of languages to add?
- Chris
On 03/25/2013 10:05 PM, Chris Lumens wrote:
pyanaconda/ui/gui/spokes/langsupport.glade | 201 +++++++++++++++++++++++++++++ pyanaconda/ui/gui/spokes/langsupport.py | 195 ++++++++++++++++++++++++++++ 2 files changed, 396 insertions(+) create mode 100644 pyanaconda/ui/gui/spokes/langsupport.glade create mode 100644 pyanaconda/ui/gui/spokes/langsupport.py
Remember to add your new files to po/POTFILES.in or they won't get translated.
Sure, thanks.
<child><object class="GtkEntry" id="langsupportEntry"><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><signal name="changed" handler="on_entry_changed" swapped="no"/><signal name="icon-release" handler="on_clear_icon_clicked" swapped="no"/></object>One thing we started doing on these kinds of widgets on the UI throwdown day last week was add secondary text to indicate this is a search box. Check out 9b632169e5b83d57d627d39f94f4315abbb9aabf for an example. I think we should do the same thing here, too.
Ok, I'll add it.
- def apply(self):
self.data.lang.addsupport = [row[COL_LANG_SETTING]for row in self._langsupportStoreif row[COL_SELECTED] and row[COL_IS_ADDITIONAL]]I was kind of imagining that this would work by adding *-support packages to the list of packages to be installed. I suppose at this point we don't necessarily have packaging set up, though, do we? Passsing through kickstart elsewhere like this is probably for the best, though.
No, not necessarily IMO. I think handling *-support groups in payload.preInstall() - as we have been doing - is fine.We'll just add additional groups in the same place. Note that we don't do dependency checking for those groups earlier in first hub (which OK I think and doing it would mean entering whole new area of pain of syncing lang support selection and source/software selection I think).
Moreover, we not only add *-support groups but also need to update configuration file for yum langpacks plugin which happens also only before installation.
You may not remember this, but a long time ago we even had a langsupport kickstart command. I wonder if it'd be better to bring that back than to add additional options to existing commands. Which way is more confusing?
Yes, I noticed the command and thought about it.One difference here is that with current approach you can't add additional support without specifying lang (system language). Is it worse or better? To me it seems less comfortable (no option to just go with default lang and add additional one with langsupport command), but I think it is also more clear in a way (e.g. is it clear that when using onlylangsupport command, default system lang will be used?).
- @property
- def status(self):
# TODO: translateinfos = set(self._find_localeinfos_for_code(self.data.lang.lang, self.locale_infos_for_data)[:1])for code in self.data.lang.addsupport:infos.update(self._find_localeinfos_for_code(code, self.locale_infos_for_data))return ", ".join(info.english_name for info in infos)What happens if I select a lot of languages to add?
They are ellipsized in the status. Do you think we should display only system lang (data.lang.lang) - similar as withdefault keyboard and keyboard spoke? Maybebut one thing is that it can't be changed in the spoke sothe status wouldn't reflect any changes in the spoke actually. Perhapswe can make the system lang (which comes first) more prominent?
Radek
Yes, I noticed the command and thought about it.One difference here is that with current approach you can't add additional support without specifying lang (system language). Is it worse or better? To me it seems less comfortable (no option to just go with default lang and add additional one with langsupport command), but I think it is also more clear in a way (e.g. is it clear that when using onlylangsupport command, default system lang will be used?).
Okay, I think having what you've originally come up with is for the best, then. If the behavior is different from the previous langsupport command, then it's going to be more confusing. Also, I don't believe we've removed a command and then brought it back later. Doing so would create this weird time where it wasn't supported, followed by different support.
It's even confusing to talk about.
What happens if I select a lot of languages to add?
They are ellipsized in the status. Do you think we should display only system lang (data.lang.lang) - similar as withdefault keyboard and keyboard spoke? Maybebut one thing is that it can't be changed in the spoke sothe status wouldn't reflect any changes in the spoke actually. Perhapswe can make the system lang (which comes first) more prominent?
Ah, I'd forgotten that the ellipses thing was global. That might read kind of funny, though. If you do want to make the system language show first, you could make the string something like: "%s and others", where %s is the system language. There might be better ways of wording that. it was just the first thing that came to mind.
- Chris
--- pykickstart/commands/lang.py | 34 ++++++++++++++++++++++++++++++++++ pykickstart/handlers/control.py | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/pykickstart/commands/lang.py b/pykickstart/commands/lang.py index dbdc852..b44bc82 100644 --- a/pykickstart/commands/lang.py +++ b/pykickstart/commands/lang.py @@ -52,3 +52,37 @@ class FC3_Lang(KickstartCommand):
self.lang = extra[0] return self + +class F19_Lang(FC3_Lang): + removedKeywords = FC3_Lang.removedKeywords + removedAttrs = FC3_Lang.removedAttrs + + def __init__(self, writePriority=0, *args, **kwargs): + FC3_Lang.__init__(self, writePriority, *args, **kwargs) + self.addsupport = kwargs.get("addsupport", []) + + self.op = self._getParser() + + def __str__(self): + s = FC3_Lang.__str__(self) + if s and self.addsupport: + s = s.rstrip() + s += " --addsupport=%s\n" % ",".join(self.addsupport) + return s + + def _getParser(self): + def list_cb (option, opt_str, value, parser): + for item in value.split(','): + if item: + parser.values.ensure_value(option.dest, []).append(item) + + op = FC3_Lang._getParser(self) + op.add_option("--addsupport", dest="addsupport", action="callback", + callback=list_cb, nargs=1, type="string") + return op + + def parse(self, args): + FC3_Lang.parse(self, args) + (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) + self._setToSelf(self.op, opts) + return self diff --git a/pykickstart/handlers/control.py b/pykickstart/handlers/control.py index 52b0a94..113de2d 100644 --- a/pykickstart/handlers/control.py +++ b/pykickstart/handlers/control.py @@ -971,7 +971,7 @@ commandMap = { "iscsi": iscsi.F17_Iscsi, "iscsiname": iscsiname.FC6_IscsiName, "keyboard": keyboard.F18_Keyboard, - "lang": lang.FC3_Lang, + "lang": lang.F19_Lang, "logging": logging.FC6_Logging, "logvol": logvol.F18_LogVol, "mediacheck": mediacheck.FC4_MediaCheck,
On Mon, 2013-03-25 at 10:22 +0100, Radek Vykydal wrote:
pykickstart/commands/lang.py | 34 ++++++++++++++++++++++++++++++++++ pykickstart/handlers/control.py | 2 +- 2 files changed, 35 insertions(+), 1 deletion(-)
diff --git a/pykickstart/commands/lang.py b/pykickstart/commands/lang.py index dbdc852..b44bc82 100644 --- a/pykickstart/commands/lang.py +++ b/pykickstart/commands/lang.py @@ -52,3 +52,37 @@ class FC3_Lang(KickstartCommand):
self.lang = extra[0] return self
+class F19_Lang(FC3_Lang):
- removedKeywords = FC3_Lang.removedKeywords
- removedAttrs = FC3_Lang.removedAttrs
- def __init__(self, writePriority=0, *args, **kwargs):
FC3_Lang.__init__(self, writePriority, *args, **kwargs)self.addsupport = kwargs.get("addsupport", [])self.op = self._getParser()- def __str__(self):
s = FC3_Lang.__str__(self)if s and self.addsupport:s = s.rstrip()s += " --addsupport=%s\n" % ",".join(self.addsupport)return s- def _getParser(self):
def list_cb (option, opt_str, value, parser):for item in value.split(','):if item:parser.values.ensure_value(option.dest, []).append(item)op = FC3_Lang._getParser(self)op.add_option("--addsupport", dest="addsupport", action="callback",callback=list_cb, nargs=1, type="string")return op- def parse(self, args):
FC3_Lang.parse(self, args)(opts, extra) = self.op.parse_args(args=args, lineno=self.lineno)self._setToSelf(self.op, opts)return selfdiff --git a/pykickstart/handlers/control.py b/pykickstart/handlers/control.py index 52b0a94..113de2d 100644 --- a/pykickstart/handlers/control.py +++ b/pykickstart/handlers/control.py @@ -971,7 +971,7 @@ commandMap = { "iscsi": iscsi.F17_Iscsi, "iscsiname": iscsiname.FC6_IscsiName, "keyboard": keyboard.F18_Keyboard,
"lang": lang.FC3_Lang,
"lang": lang.F19_Lang, "logging": logging.FC6_Logging, "logvol": logvol.F18_LogVol, "mediacheck": mediacheck.FC4_MediaCheck,
Please, add a test for this new option to tests/commands/lang.py
--- pykickstart/commands/lang.py | 34 ++++++++++++++++++++++++++++++++++ pykickstart/handlers/control.py | 2 +- tests/commands/lang.py | 17 +++++++++++++++++ 3 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/pykickstart/commands/lang.py b/pykickstart/commands/lang.py index dbdc852..b44bc82 100644 --- a/pykickstart/commands/lang.py +++ b/pykickstart/commands/lang.py @@ -52,3 +52,37 @@ class FC3_Lang(KickstartCommand):
self.lang = extra[0] return self + +class F19_Lang(FC3_Lang): + removedKeywords = FC3_Lang.removedKeywords + removedAttrs = FC3_Lang.removedAttrs + + def __init__(self, writePriority=0, *args, **kwargs): + FC3_Lang.__init__(self, writePriority, *args, **kwargs) + self.addsupport = kwargs.get("addsupport", []) + + self.op = self._getParser() + + def __str__(self): + s = FC3_Lang.__str__(self) + if s and self.addsupport: + s = s.rstrip() + s += " --addsupport=%s\n" % ",".join(self.addsupport) + return s + + def _getParser(self): + def list_cb (option, opt_str, value, parser): + for item in value.split(','): + if item: + parser.values.ensure_value(option.dest, []).append(item) + + op = FC3_Lang._getParser(self) + op.add_option("--addsupport", dest="addsupport", action="callback", + callback=list_cb, nargs=1, type="string") + return op + + def parse(self, args): + FC3_Lang.parse(self, args) + (opts, extra) = self.op.parse_args(args=args, lineno=self.lineno) + self._setToSelf(self.op, opts) + return self diff --git a/pykickstart/handlers/control.py b/pykickstart/handlers/control.py index 52b0a94..113de2d 100644 --- a/pykickstart/handlers/control.py +++ b/pykickstart/handlers/control.py @@ -971,7 +971,7 @@ commandMap = { "iscsi": iscsi.F17_Iscsi, "iscsiname": iscsiname.FC6_IscsiName, "keyboard": keyboard.F18_Keyboard, - "lang": lang.FC3_Lang, + "lang": lang.F19_Lang, "logging": logging.FC6_Logging, "logvol": logvol.F18_LogVol, "mediacheck": mediacheck.FC4_MediaCheck, diff --git a/tests/commands/lang.py b/tests/commands/lang.py index b78e4da..d5352e4 100644 --- a/tests/commands/lang.py +++ b/tests/commands/lang.py @@ -17,5 +17,22 @@ class FC3_TestCase(CommandTest): self.assert_parse_error("lang", KickstartValueError) self.assert_parse_error("lang en_US en_CA", KickstartValueError)
+class F19_TestCase(FC3_TestCase): + command = "lang" + + def runTest(self): + + # pass + self.assert_parse("lang en_US") + self.assert_parse("lang en_US --addsupport=cs_CZ") + self.assert_parse("lang en_US --addsupport=sr_RS.UTF-8@latin") + self.assert_parse("lang en_US --addsupport=cs_CZ,fr_FR") + + # fail + # Fail if less than or more than one argument is specified + self.assert_parse_error("lang", KickstartValueError) + self.assert_parse_error("lang en_US en_CA", KickstartValueError) + self.assert_parse_error("lang --addsupport=en_US", KickstartValueError) + if __name__ == "__main__": unittest.main()
On Mon, 2013-03-25 at 10:22 +0100, Radek Vykydal wrote:
Kickstart command lang is extended with --addsupport option for specifying additional (to ksdata.lang.lang) language support to be installed (ksdata.lang.addsupport).
In the spoke GUI, ksdata.lang.lang (system lang) is selected and grayed-out (insensitive).
Selected (using checkbox) languages are displayed in bold, the list is searchable in the same way as the welcome spoke list. Currently only available translations for installer (the same as in welcome spoke) are in the list.
screencast: http://rvykydal.fedorapeople.org/langsupport.ogg
Apart from the minor comments the code looks good to me. And just one note for the behaviour -- could we reset the filtering entry text to "" in the refresh method, so that the whole list appears when the spoke is revisited?
On 03/25/2013 01:22 PM, Vratislav Podzimek wrote:
Apart from the minor comments the code looks good to me. And just one note for the behaviour -- could we reset the filtering entry text to "" in the refresh method, so that the whole list appears when the spoke is revisited?
Yes, good idea.
anaconda-patches@lists.fedorahosted.org