The following pile of patches fixes up most of the translation issues throughout anaconda, basically for bug 858628. I've not applied that number to any individual fix as it's hard to really tell what should get the bug.
The following things still are not translated:
- Cancel and Add buttons on the keyboard's add dialog. - Close button on the keyboard's layout dialog. - Remove and Close buttons on the disk shopping cart. - Cancel button on the reclaim dialog. - Root names on the custom partitioning's LHS. - All of the select ISO dialog. - All of python-meh, except for the Quit button.
Anyone want to take a look at those?
- Chris
It doesn't work completely, and isn't needed now that we do not support changing language from the hub via the language spoke. --- pyanaconda/ui/common.py | 7 ------- pyanaconda/ui/gui/__init__.py | 41 ------------------------------------- pyanaconda/ui/gui/spokes/welcome.py | 8 -------- pyanaconda/ui/tui/tuiobject.py | 9 -------- 4 files changed, 65 deletions(-)
diff --git a/pyanaconda/ui/common.py b/pyanaconda/ui/common.py index 1a0a979..bcac8b4 100644 --- a/pyanaconda/ui/common.py +++ b/pyanaconda/ui/common.py @@ -57,13 +57,6 @@ class UIObject(object): """ pass
- def retranslate(self): - """This method should be called when the current language is changed - in order to update the UI for the new language. Since we don't get - any toolkit help for this, it is largely a manual process. - """ - pass - def refresh(self): """Perform whatever actions are necessary to reset the UI immediately before it is displayed. This method is called every time a screen diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 52336f8..ba96523 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -302,14 +302,6 @@ class GUIObject(common.UIObject): if self.__class__ is GUIObject: raise TypeError("GUIObject is an abstract class")
- # This couldn't possibly be a bigger hack job. This structure holds the - # untranslated strings out of each widget. retranslate works by taking the - # string out of a widget, translating it, and then cramming it back into - # the widget. When we go to change language a second time, the fetched - # string will be the translated one. Strings in gettext are keyed on the - # original English, so we'd be looking up translations by translations. - self._origStrings = {} - self.skipTo = None self.applyOnSkip = False
@@ -357,39 +349,6 @@ class GUIObject(common.UIObject):
_screenshotIndex += 1
- def retranslate(self): - """This method should be called when the current language is changed - in order to update the UI for the new language. Since we don't get - any toolkit help for this, it is largely a manual process. - """ - from gi.repository import AnacondaWidgets, Gtk - - # NOTE: If you see widgets on a screen that remain untranslated even - # when you know there are translations, the widgets likely need to be - # added to this dict. - # Widget class -> (getter, setter) -or- - # Widget class -> (setter, ) - widgetMap = { AnacondaWidgets.StandaloneWindow: ("retranslate", ), - Gtk.Button: ("get_label", "set_label"), - Gtk.Label: ("get_label", "set_label") } - classes = widgetMap.keys() - - objs = filter(lambda obj: obj.__class__ in classes, self.builder.get_objects()) - for obj in objs: - klass = obj.__class__ - funcs = widgetMap[klass] - - if len(funcs) == 1: - getattr(obj, funcs[0])() - else: - # Only store the string once, so we make sure to get the original. - if not obj in self._origStrings: - self._origStrings[obj] = getattr(obj, funcs[0])() - - before = self._origStrings[obj] - xlated = _(before) - getattr(obj, funcs[1])(xlated) - @property def window(self): """Return the top-level object out of the GtkBuilder representation diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 7362a18..71093c7 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -233,10 +233,6 @@ class WelcomeLanguageSpoke(LanguageMixIn, StandaloneSpoke): StandaloneSpoke.__init__(self, *args) LanguageMixIn.__init__(self)
- def retranslate(self): - StandaloneSpoke.retranslate(self) - LanguageMixIn.retranslate(self) - def refresh(self): StandaloneSpoke.refresh(self) LanguageMixIn.refresh(self, "welcomeWindowContentBox") @@ -283,10 +279,6 @@ class LanguageSpoke(LanguageMixIn, NormalSpoke): LanguageMixIn.initialize(self) NormalSpoke.initialize(self)
- def retranslate(self): - NormalSpoke.retranslate(self) - LanguageMixIn.retranslate(self) - def refresh(self): NormalSpoke.refresh(self) LanguageMixIn.refresh(self, "languageSpokeWindowContentBox") diff --git a/pyanaconda/ui/tui/tuiobject.py b/pyanaconda/ui/tui/tuiobject.py index 35c920f..42a0847 100644 --- a/pyanaconda/ui/tui/tuiobject.py +++ b/pyanaconda/ui/tui/tuiobject.py @@ -48,12 +48,3 @@ class TUIObject(tui.UIScreen, common.UIObject): def refresh(self, args = None): """Put everything to display into self.window list.""" tui.UIScreen.refresh(self, args) - - def retranslate(self): - """After language is changed, this method ensures that all the - texts on screen are translated. It only needs to refresh the - screen in text mode, as translation will happen automatically - and there is no way to change labels on previously displayed content.""" - - # redraw - self.app.switch_screen(self)
It might as well do it on its own. --- pyanaconda/ui/gui/spokes/welcome.glade | 4 ++-- pyanaconda/ui/gui/spokes/welcome.py | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/welcome.glade b/pyanaconda/ui/gui/spokes/welcome.glade index b9761b9..f0c63d9 100644 --- a/pyanaconda/ui/gui/spokes/welcome.glade +++ b/pyanaconda/ui/gui/spokes/welcome.glade @@ -59,7 +59,7 @@ <property name="row_spacing">12</property> <property name="column_spacing">6</property> <child> - <object class="GtkLabel" id="label1"> + <object class="GtkLabel" id="betaWarnTitle"> <property name="visible">True</property> <property name="can_focus">False</property> <property name="halign">start</property> @@ -91,7 +91,7 @@ </packing> </child> <child> - <object class="GtkLabel" id="label2"> + <object class="GtkLabel" id="betaWarnDesc"> <property name="width_request">300</property> <property name="visible">True</property> <property name="can_focus">False</property> diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 71093c7..5668d1e 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -136,7 +136,26 @@ class LanguageMixIn(object):
self._languageStoreFilter.set_visible_func(self._matchesEntry, None)
+ def _retranslate_one(self, widgetName): + widget = self.builder.get_object(widgetName) + if not widget: + return + + if not widget in self._origStrings: + self._origStrings[widget] = widget.get_label() + + before = self._origStrings[widget] + widget.set_label(_(before)) + def retranslate(self): + # Change the translations on labels and buttons that do not have + # substitution text. + for name in ["pickLanguageLabel", "betaWarnTitle", "betaWarnDesc", + "quitButton", "continueButton"]: + self._retranslate_one(name) + + # The welcome label is special - it has text that needs to be + # substituted. welcomeLabel = self.builder.get_object(self._labelName)
if not welcomeLabel in self._origStrings: @@ -146,6 +165,9 @@ class LanguageMixIn(object): xlated = _(before) % (productName.upper(), productVersion) welcomeLabel.set_label(xlated)
+ # And of course, don't forget the underlying window. + self.window.retranslate() + def refresh(self, displayArea): store = self.builder.get_object("languageStore") self._selectLanguage(store, self.data.lang.lang)
On Fri, 2012-11-30 at 11:28 -0500, Chris Lumens wrote:
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 71093c7..5668d1e 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -136,7 +136,26 @@ class LanguageMixIn(object):
self._languageStoreFilter.set_visible_func(self._matchesEntry, None)
- def _retranslate_one(self, widgetName):
widget = self.builder.get_object(widgetName)if not widget:returnif not widget in self._origStrings:self._origStrings[widget] = widget.get_label()before = self._origStrings[widget]widget.set_label(_(before))
Too much indentation here?
Without this check, ENABLE_NLS will not be defined, which means gettext.h will not do anything, which means strings in C lang will not be translated no matter how hard we try. --- widgets/configure.ac | 4 +++- widgets/src/intl.h | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/widgets/configure.ac b/widgets/configure.ac index eb85827..9df0ed7 100644 --- a/widgets/configure.ac +++ b/widgets/configure.ac @@ -36,6 +36,8 @@ AC_PROG_CXX AC_PROG_LIBTOOL AC_PROG_LN_S
+AM_GNU_GETTEXT([external]) + GOBJECT_INTROSPECTION_CHECK([0.6.7])
m4_ifdef([GTK_DOC_CHECK], [ @@ -50,7 +52,7 @@ PKG_CHECK_MODULES([GTK], [gtk+-x11-3.0 >= 3.0]) PKG_CHECK_MODULES([GLIB], [glib-2.0]) PKG_CHECK_EXISTS([gobject-introspection-1.0 >= 1.30])
-AC_CHECK_HEADERS([string.h]) +AC_CHECK_HEADERS([libintl.h string.h])
AC_CONFIG_FILES([Makefile doc/Makefile diff --git a/widgets/src/intl.h b/widgets/src/intl.h index b39ee60..6f8b3d5 100644 --- a/widgets/src/intl.h +++ b/widgets/src/intl.h @@ -27,7 +27,7 @@ #define N_(String) String
#ifdef ENABLE_NLS -#define P_(String) g_dgettext(GETTEXT_PACKAGE "-properties",String) +#define P_(String) g_dgettext("anaconda-properties",String) #else #define P_(String) (String) #endif
Everything in the beginning of anaconda_base_window_retranslate is critical to making sure we can change translations at runtime. The textdomain call could go somewhere else such that it only happens once when the library is loaded, but it's not doing any harm where it is (for now).
Translations are still not totally working, but this gets us a lot closer. --- pyanaconda/ui/gui/spokes/welcome.py | 6 +++--- widgets/src/BaseWindow.c | 17 ++++++++++++++++- widgets/src/BaseWindow.h | 2 +- widgets/src/StandaloneWindow.c | 4 ++-- widgets/src/StandaloneWindow.h | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index 5668d1e..e2aa78b 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -147,7 +147,7 @@ class LanguageMixIn(object): before = self._origStrings[widget] widget.set_label(_(before))
- def retranslate(self): + def retranslate(self, lang): # Change the translations on labels and buttons that do not have # substitution text. for name in ["pickLanguageLabel", "betaWarnTitle", "betaWarnDesc", @@ -166,7 +166,7 @@ class LanguageMixIn(object): welcomeLabel.set_label(xlated)
# And of course, don't forget the underlying window. - self.window.retranslate() + self.window.retranslate(lang)
def refresh(self, displayArea): store = self.builder.get_object("languageStore") @@ -234,7 +234,7 @@ class LanguageMixIn(object): lang = store[selected[0]][2] self.language.set_install_lang(lang) self.language.set_system_lang(lang) - self.retranslate() + self.retranslate(lang)
def on_clear_icon_clicked(self, entry, icon_pos, event): if icon_pos == Gtk.EntryIconPosition.SECONDARY: diff --git a/widgets/src/BaseWindow.c b/widgets/src/BaseWindow.c index 8b3fcc3..ff21a8d 100644 --- a/widgets/src/BaseWindow.c +++ b/widgets/src/BaseWindow.c @@ -17,6 +17,8 @@ * Author: Chris Lumens clumens@redhat.com */
+#include <libintl.h> +#include <stdlib.h> #include <string.h>
#include "BaseWindow.h" @@ -522,10 +524,23 @@ void anaconda_base_window_clear_info(AnacondaBaseWindow *win) { * * Since: 1.0 */ -void anaconda_base_window_retranslate(AnacondaBaseWindow *win) { +void anaconda_base_window_retranslate(AnacondaBaseWindow *win, const char *lang) { char *markup; GValue distro = G_VALUE_INIT;
+ if (!textdomain("anaconda")) { + fprintf(stderr, "textdomain failed\n"); + return; + } + + setenv("LANGUAGE", lang, 1); + setlocale(LC_ALL, ""); + + { + extern int _nl_msg_cat_cntr; + ++_nl_msg_cat_cntr; + } + g_value_init(&distro, G_TYPE_STRING); g_value_set_string(&distro, _(win->priv->orig_distro));
diff --git a/widgets/src/BaseWindow.h b/widgets/src/BaseWindow.h index 3dd82bc..ef9c210 100644 --- a/widgets/src/BaseWindow.h +++ b/widgets/src/BaseWindow.h @@ -66,7 +66,7 @@ struct _AnacondaBaseWindowClass { GType anaconda_base_window_get_type (void); GtkWidget *anaconda_base_window_new ();
-void anaconda_base_window_retranslate (AnacondaBaseWindow *win); +void anaconda_base_window_retranslate (AnacondaBaseWindow *win, const char *lang);
gboolean anaconda_base_window_get_beta (AnacondaBaseWindow *win); void anaconda_base_window_set_beta (AnacondaBaseWindow *win, gboolean is_beta); diff --git a/widgets/src/StandaloneWindow.c b/widgets/src/StandaloneWindow.c index 5d68f8e..d7e86a8 100644 --- a/widgets/src/StandaloneWindow.c +++ b/widgets/src/StandaloneWindow.c @@ -205,8 +205,8 @@ void anaconda_standalone_window_set_may_continue(AnacondaStandaloneWindow *win, * * Since: 1.0 */ -void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win) { - anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win)); +void anaconda_standalone_window_retranslate(AnacondaStandaloneWindow *win, const char *lang) { + anaconda_base_window_retranslate(ANACONDA_BASE_WINDOW(win), lang); gtk_button_set_label(GTK_BUTTON(win->priv->quit_button), _(QUIT_TEXT)); gtk_button_set_label(GTK_BUTTON(win->priv->continue_button), _(CONTINUE_TEXT)); } diff --git a/widgets/src/StandaloneWindow.h b/widgets/src/StandaloneWindow.h index 6afce6f..d156205 100644 --- a/widgets/src/StandaloneWindow.h +++ b/widgets/src/StandaloneWindow.h @@ -72,7 +72,7 @@ GType anaconda_standalone_window_get_type (void); GtkWidget *anaconda_standalone_window_new (); gboolean anaconda_standalone_window_get_may_continue (AnacondaStandaloneWindow *win); void anaconda_standalone_window_set_may_continue (AnacondaStandaloneWindow *win, gboolean may_continue); -void anaconda_standalone_window_retranslate (AnacondaStandaloneWindow *win); +void anaconda_standalone_window_retranslate (AnacondaStandaloneWindow *win, const char *lang);
G_END_DECLS
The textdomain call could go somewhere else such that it only happens once when the library is loaded, but it's not doing any harm where it is (for now).
In fact, I can ditch the textdomain call and instead do the following:
diff --git a/widgets/src/intl.h b/widgets/src/intl.h index 6f8b3d5..18aad9f 100644 --- a/widgets/src/intl.h +++ b/widgets/src/intl.h @@ -23,7 +23,7 @@ #include "../config.h" #include "gettext.h"
-#define _(x) gettext(x) +#define _(x) dgettext("anaconda", x) #define N_(String) String
#ifdef ENABLE_NLS
- Chris
We don't want to re-set up installation source just because someone's using a different language than we expect. --- pyanaconda/ui/gui/spokes/source.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/source.py b/pyanaconda/ui/gui/spokes/source.py index 33518b5..0bc99a2 100644 --- a/pyanaconda/ui/gui/spokes/source.py +++ b/pyanaconda/ui/gui/spokes/source.py @@ -786,16 +786,16 @@ class SourceSpoke(NormalSpoke): return not flags.livecdInstall
def _mirror_active(self): - return self._protocolComboBox.get_active_text().startswith("Closest") + return self._protocolComboBox.get_active() == 0
def _http_active(self): - return self._protocolComboBox.get_active_text().startswith("http") + return self._protocolComboBox.get_active() in [1, 2]
def _ftp_active(self): - return self._protocolComboBox.get_active_text().startswith("ftp") + return self._protocolComboBox.get_active() == 3
def _nfs_active(self): - return self._protocolComboBox.get_active_text().startswith("nfs") + return self._protocolComboBox.get_active() == 4
def _get_selected_media(self): dev = None
What we've got now is a string names new_install_name with the product and version substituted into it, then that string being looked up for translation. There's no string that matches, of course, so the new install header on the LHS is not being displayed translated. --- pyanaconda/ui/gui/spokes/custom.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-)
diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index eeacf8e..a487531 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -91,7 +91,7 @@ NOTEBOOK_DETAILS_PAGE = 1 NOTEBOOK_LUKS_PAGE = 2 NOTEBOOK_UNEDITABLE_PAGE = 3
-new_install_name = N_("New %s %s Installation") % (productName, productVersion) +new_install_name = N_("New %s %s Installation") unrecoverable_error_msg = N_("Storage configuration reset due to unrecoverable " "error. Click for details.") device_configuration_error_msg = N_("Device reconfiguration failed. Click for " @@ -570,6 +570,10 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
self._updateSpaceDisplay()
+ @property + def translated_new_install_name(self): + return _(new_install_name) % (productName, productVersion) + def _do_refresh(self): # block mountpoint selector signal handler for now self._initialized = False @@ -617,7 +621,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): # ensures it's only added once. if not new_devices: page = CreateNewPage(self.on_create_clicked) - page.pageTitle = _(new_install_name) + page.pageTitle = self.translated_new_install_name self._accordion.addPage(page, cb=self.on_page_clicked)
if page.pageTitle not in page_order: @@ -635,7 +639,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): if device in self.bootLoaderDevices: mounts[device.format.type] = device
- new_root = Root(mounts=mounts, swaps=swaps, name=_(new_install_name)) + new_root = Root(mounts=mounts, swaps=swaps, name=self.translated_new_install_name) ui_roots.insert(0, new_root)
# Add in all the existing (or autopart-created) operating systems. @@ -644,7 +648,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): # Also, only include devices in an old page if the format is intact. if not [d for d in root.swaps + root.mounts.values() if d in self._devices and d.disks and - (root.name == _(new_install_name) or d.format.exists)]: + (root.name == self.translated_new_install_name or d.format.exists)]: continue
page = Page() @@ -653,7 +657,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): for (mountpoint, device) in root.mounts.iteritems(): if device not in self._devices or \ not device.disks or \ - (root.name != _(new_install_name) and not device.format.exists): + (root.name != self.translated_new_install_name and not device.format.exists): continue
selector = page.addDevice(self._mountpointName(mountpoint) or device.format.name, Size(spec="%f MB" % device.size), mountpoint, self.on_selector_clicked) @@ -662,7 +666,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
for device in root.swaps: if device not in self._devices or \ - (root.name != _(new_install_name) and not device.format.exists): + (root.name != self.translated_new_install_name and not device.format.exists): continue
selector = page.addDevice("Swap", @@ -731,15 +735,15 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
def add_new_selector(self, device): """ Add an entry for device to the new install Page. """ - page = self._accordion._find_by_title(_(new_install_name)).get_child() + page = self._accordion._find_by_title(self.translated_new_install_name).get_child() devices = [device] if not hasattr(page, "_members"): # remove the CreateNewPage and replace it with a regular Page - expander = self._accordion._find_by_title(_(new_install_name)) + expander = self._accordion._find_by_title(self.translated_new_install_name) expander.remove(expander.get_child())
page = Page() - page.pageTitle = _(new_install_name) + page.pageTitle = self.translated_new_install_name expander.add(page)
# pull in all the existing swap devices @@ -765,7 +769,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): def _update_btrfs_selectors(self): """ Update all btrfs selectors' size properties. """ # we're only updating selectors in the new root. problem? - page = self._accordion._find_by_title(_(new_install_name)).get_child() + page = self._accordion._find_by_title(self.translated_new_install_name).get_child() if not hasattr(page, "_members"): return
@@ -1135,7 +1139,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): for page in self._accordion.allPages: for _selector in getattr(page, "_members", []): if _selector._device in (device, old_device): - if page.pageTitle == _(new_install_name): + if page.pageTitle == self.translated_new_install_name: new_selector = _selector continue
@@ -1720,7 +1724,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
log.debug("removing device '%s' from page %s" % (device, root_name))
- if root_name == _(new_install_name): + if root_name == self.translated_new_install_name: if device.exists: # This is an existing device that was added to the new page. # All we want to do is revert any changes to the device and
Except on the hub, which is special and is going to need special work done for it. --- pyanaconda/product.py | 7 +++++++ pyanaconda/ui/gui/__init__.py | 9 ++++----- pyanaconda/ui/gui/hubs/__init__.py | 5 ++--- pyanaconda/ui/gui/spokes/welcome.py | 3 ++- widgets/src/BaseWindow.c | 4 ++-- 5 files changed, 17 insertions(+), 11 deletions(-)
diff --git a/pyanaconda/product.py b/pyanaconda/product.py index c269b3e..0edab7d 100644 --- a/pyanaconda/product.py +++ b/pyanaconda/product.py @@ -20,6 +20,9 @@ import ConfigParser import os
+import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + # First, load in the defaults. In order of precedence: contents of # .buildstamp, environment, stupid last ditch hardcoded defaults. config = ConfigParser.ConfigParser() @@ -46,3 +49,7 @@ if not productArch and productStamp.index(".") != -1: productArch = productStamp[productStamp.index(".")+1:] if productVersion == "development": productVersion = "rawhide" + +def distributionText(): + return _("%(productName)s %(productVersion)s INSTALLATION") % \ + {"productName": productName, "productVersion": productVersion} diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index ba96523..15116d2 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -23,6 +23,8 @@ import meh.ui.gui
from gi.repository import Gdk
+from pyanaconda.product import distributionText, isFinal + from pyanaconda.ui import UserInterface, common from pyanaconda.ui.gui.utils import enlightbox, gtk_thread_wait
@@ -100,16 +102,13 @@ class GraphicalUserInterface(UserInterface):
sys.exit(0)
- from pyanaconda.product import isFinal, productName, productVersion - # If we set these values on the very first window shown, they will get # propagated to later ones. self._actions[0].initialize() self._actions[0].refresh()
self._actions[0].window.set_beta(not isFinal) - self._actions[0].window.set_property("distribution", _("%(productName)s %(productVersion)s INSTALLATION") % \ - {"productName": productName.upper(), "productVersion": productVersion}) + self._actions[0].window.set_property("distribution", distributionText().upper())
# Set fonts app-wide, where possible settings = Gtk.Settings.get_default() @@ -216,7 +215,7 @@ class GraphicalUserInterface(UserInterface):
self._actions[1].initialize() self._actions[1].window.set_beta(self._actions[0].window.get_beta()) - self._actions[1].window.set_property("distribution", self._actions[0].window.get_property("distribution")) + self._actions[1].window.set_property("distribution", distributionText().upper())
if not self._actions[1].showable: self._actions[0].window.hide() diff --git a/pyanaconda/ui/gui/hubs/__init__.py b/pyanaconda/ui/gui/hubs/__init__.py index f702fa6..c0df863 100644 --- a/pyanaconda/ui/gui/hubs/__init__.py +++ b/pyanaconda/ui/gui/hubs/__init__.py @@ -26,6 +26,7 @@ _ = lambda x: gettext.ldgettext("anaconda", x) from gi.repository import GLib
from pyanaconda.flags import flags +from pyanaconda.product import distributionText
from pyanaconda.ui import common from pyanaconda.ui.gui import GUIObject @@ -93,10 +94,8 @@ class Hub(GUIObject, common.Hub):
action.refresh()
- # Set various properties on the new Spoke based upon what was set - # on the Hub. action.window.set_beta(self.window.get_beta()) - action.window.set_property("distribution", self.window.get_property("distribution")) + action.window.set_property("distribution", distributionText().upper())
action.window.set_transient_for(self.window) action.window.show_all() diff --git a/pyanaconda/ui/gui/spokes/welcome.py b/pyanaconda/ui/gui/spokes/welcome.py index e2aa78b..6644ea9 100644 --- a/pyanaconda/ui/gui/spokes/welcome.py +++ b/pyanaconda/ui/gui/spokes/welcome.py @@ -34,7 +34,7 @@ from pyanaconda.ui.gui.utils import enlightbox from pyanaconda.ui.gui.categories.localization import LocalizationCategory
from pyanaconda.localization import Language, LOCALE_PREFERENCES, expand_langs -from pyanaconda.product import isFinal, productName, productVersion +from pyanaconda.product import distributionText, isFinal, productName, productVersion from pyanaconda import keyboard from pyanaconda import timezone from pyanaconda import flags @@ -166,6 +166,7 @@ class LanguageMixIn(object): welcomeLabel.set_label(xlated)
# And of course, don't forget the underlying window. + self.window.set_property("distribution", distributionText().upper()) self.window.retranslate(lang)
def refresh(self, displayArea): diff --git a/widgets/src/BaseWindow.c b/widgets/src/BaseWindow.c index ff21a8d..b23e0c9 100644 --- a/widgets/src/BaseWindow.c +++ b/widgets/src/BaseWindow.c @@ -290,7 +290,7 @@ static void anaconda_base_window_set_property(GObject *object, guint prop_id, co
switch(prop_id) { case PROP_DISTRIBUTION: { - char *markup = g_markup_printf_escaped("<span size='large'>%s</span>", g_value_get_string(value)); + char *markup = g_markup_printf_escaped("<span size='large'>%s</span>", _(g_value_get_string(value))); gtk_label_set_markup(GTK_LABEL(priv->distro_label), markup); g_free(markup);
@@ -301,7 +301,7 @@ static void anaconda_base_window_set_property(GObject *object, guint prop_id, co }
case PROP_WINDOW_NAME: { - char *markup = g_markup_printf_escaped("<span weight='bold' size='large'>%s</span>", g_value_get_string(value)); + char *markup = g_markup_printf_escaped("<span weight='bold' size='large'>%s</span>", _(g_value_get_string(value))); gtk_label_set_markup(GTK_LABEL(priv->name_label), markup); g_free(markup);
--- po/POTFILES.in | 1 + 1 file changed, 1 insertion(+)
diff --git a/po/POTFILES.in b/po/POTFILES.in index c315900..0009665 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -78,6 +78,7 @@ pyanaconda/ui/gui/categories/__init__.py pyanaconda/ui/gui/categories/localization.py pyanaconda/ui/gui/categories/software.py pyanaconda/ui/gui/categories/storage.py +pyanaconda/ui/gui/categories/user_settings.py pyanaconda/ui/gui/hubs/__init__.py pyanaconda/ui/gui/hubs/progress.py pyanaconda/ui/gui/spokes/custom.py
This prevents their strings from being translated before we know what language to translate them into, thus skipping the need for the whole retranslate thing. --- pyanaconda/ui/gui/__init__.py | 105 +++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-)
diff --git a/pyanaconda/ui/gui/__init__.py b/pyanaconda/ui/gui/__init__.py index 15116d2..eb70138 100644 --- a/pyanaconda/ui/gui/__init__.py +++ b/pyanaconda/ui/gui/__init__.py @@ -46,9 +46,11 @@ class GraphicalUserInterface(UserInterface): UserInterface.__init__(self, storage, payload, instclass)
self._actions = [] - self._hubs = [] + self._currentAction = None self._ui = None
+ self.data = None + # This is a hack to make sure the AnacondaWidgets library gets loaded # before the introspection stuff. import ctypes @@ -61,40 +63,40 @@ class GraphicalUserInterface(UserInterface):
busyCursor()
- self._hubs.extend([SummaryHub, ProgressHub]) - - # First, grab a list of all the standalone spokes. + hubs = [SummaryHub, ProgressHub] path = os.path.join(os.path.dirname(__file__), "spokes") - actionClasses = self.getActionClasses("pyanaconda.ui.gui.spokes.%s", path, self._hubs, StandaloneSpoke)
- # Instantiate all hubs and their pre/post standalone spokes, passing - # the arguments defining our spoke API and setting up continue/quit - # signal handlers. - for klass in actionClasses: - obj = klass(data, self.storage, self.payload, self.instclass) + self._actions = self.getActionClasses("pyanaconda.ui.gui.spokes.%s", path, hubs, StandaloneSpoke) + self.data = data + + def _instantiateAction(self, actionClass): + from spokes import StandaloneSpoke + + # Instantiate an action on-demand, passing the arguments defining our + # spoke API and setting up continue/quit signal handlers. + obj = actionClass(self.data, self.storage, self.payload, self.instclass)
- # If we are doing a kickstart install, some standalone spokes - # could already be filled out. In taht case, we do not want - # to display them. - if isinstance(obj, StandaloneSpoke) and obj.completed: - del(obj) - continue + # If we are doing a kickstart install, some standalone spokes + # could already be filled out. In taht case, we do not want + # to display them. + if isinstance(obj, StandaloneSpoke) and obj.completed: + del(obj) + return None
- obj.register_event_cb("continue", self._on_continue_clicked) - obj.register_event_cb("quit", self._on_quit_clicked) + obj.register_event_cb("continue", self._on_continue_clicked) + obj.register_event_cb("quit", self._on_quit_clicked)
- self._actions.append(obj) + return obj
def run(self): from gi.repository import Gtk
- unbusyCursor() - if Gtk.main_level() > 0: # Gtk main loop running. That means python-meh caught exception # and runs its main loop. Do not crash Gtk by running another one # from a different thread and just wait until python-meh is # finished, then quit. + unbusyCursor() log.error("Unhandled exception caught, waiting for python-meh to "\ "exit") while Gtk.main_level() > 0: @@ -102,19 +104,26 @@ class GraphicalUserInterface(UserInterface):
sys.exit(0)
- # If we set these values on the very first window shown, they will get - # propagated to later ones. - self._actions[0].initialize() - self._actions[0].refresh() + while not self._currentAction: + self._currentAction = self._instantiateAction(self._actions[0]) + if not self._currentAction: + self._actions.pop(0) + + self._currentAction.initialize() + + # Do this at the last possible minute. + unbusyCursor() + + self._currentAction.refresh()
- self._actions[0].window.set_beta(not isFinal) - self._actions[0].window.set_property("distribution", distributionText().upper()) + self._currentAction.window.set_beta(not isFinal) + self._currentAction.window.set_property("distribution", distributionText().upper())
# Set fonts app-wide, where possible settings = Gtk.Settings.get_default() settings.set_property("gtk-font-name", "Cantarell")
- self._actions[0].window.show_all() + self._currentAction.window.show_all() Gtk.main()
### @@ -130,7 +139,7 @@ class GraphicalUserInterface(UserInterface): dlg.set_decorated(False) dlg.add_button(_("_Exit Installer"), 0)
- with enlightbox(self._actions[0].window, dlg): + with enlightbox(self._currentAction.window, dlg): dlg.run() dlg.destroy()
@@ -140,7 +149,7 @@ class GraphicalUserInterface(UserInterface): dlg = DetailedErrorDialog(None, buttons=[_("_Quit")], label=message)
- with enlightbox(self._actions[0].window, dlg.window): + with enlightbox(self._currentAction.window, dlg.window): dlg.refresh(details) rc = dlg.run() dlg.window.destroy() @@ -156,7 +165,7 @@ class GraphicalUserInterface(UserInterface): dlg.add_buttons(_("_No"), 0, _("_Yes"), 1) dlg.set_default_response(1)
- with enlightbox(self._actions[0].window, dlg): + with enlightbox(self._currentAction.window, dlg): rc = dlg.run() dlg.destroy()
@@ -172,7 +181,7 @@ class GraphicalUserInterface(UserInterface):
# exception may appear before self._actions gets populated if len(self._actions) > 0: - lightbox = AnacondaWidgets.lb_show_over(self._actions[0].window) + lightbox = AnacondaWidgets.lb_show_over(self._currentAction.window) exc_window.main_window.set_transient_for(lightbox)
# without WindowGroup, python-meh's window is insensitive if it appears @@ -195,14 +204,15 @@ class GraphicalUserInterface(UserInterface): sys.exit(0) return
+ nextAction = None ndx = 0
# If the current action wants us to jump to an arbitrary point ahead, # look for where that is now. - if self._actions[0].skipTo: + if self._currentAction.skipTo: found = False for ndx in range(1, len(self._actions)): - if self._actions[ndx].__class__.__name__ == self._actions[0].skipTo: + if self._actions[ndx].__class__.__name__ == self._currentAction.skipTo: found = True break
@@ -213,27 +223,36 @@ class GraphicalUserInterface(UserInterface): if found: self._actions = [self._actions[0]] + self._actions[ndx:]
- self._actions[1].initialize() - self._actions[1].window.set_beta(self._actions[0].window.get_beta()) - self._actions[1].window.set_property("distribution", distributionText().upper()) + # _instantiateAction returns None for actions that should not be + # displayed (because they're already completed, for instance) so skip + # them here. + while not nextAction: + nextAction = self._instantiateAction(self._actions[1]) + if not nextAction: + self._actions.pop(1) + + nextAction.initialize() + nextAction.window.set_beta(self._currentAction.window.get_beta()) + nextAction.window.set_property("distribution", distributionText().upper())
- if not self._actions[1].showable: - self._actions[0].window.hide() + if not nextAction.showable: + self._currentAction.window.hide() self._actions.pop(0) self._on_continue_clicked() return
- self._actions[1].refresh() + nextAction.refresh()
# Do this last. Setting up curAction could take a while, and we want # to leave something on the screen while we work. - self._actions[1].window.show_all() - self._actions[0].window.hide() + nextAction.window.show_all() + self._currentAction.window.hide() + self._currentAction = nextAction self._actions.pop(0)
def _on_quit_clicked(self): dialog = QuitDialog(None) - with enlightbox(self._actions[0].window, dialog.window): + with enlightbox(self._currentAction.window, dialog.window): rc = dialog.run() dialog.window.destroy()
On Fri, Nov 30, 2012 at 11:28:25AM -0500, Chris Lumens wrote:
This prevents their strings from being translated before we know what language to translate them into, thus skipping the need for the whole retranslate thing.
pyanaconda/ui/gui/__init__.py | 105 +++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 43 deletions(-)
# If we are doing a kickstart install, some standalone spokes# could already be filled out. In taht case, we do not want
typo of taht carried forward.
while not self._currentAction:self._currentAction = self._instantiateAction(self._actions[0])if not self._currentAction:self._actions.pop(0)
I worry about this getting stuck. Nothing concrete, but maybe we should break or throw an error if _actions is empty?
while not nextAction:nextAction = self._instantiateAction(self._actions[1])if not nextAction:self._actions.pop(1)
Similar worry here.
other than that, ACK to the whole set. Nice work!
typo of taht carried forward.
The death of fun.
while not self._currentAction:self._currentAction = self._instantiateAction(self._actions[0])if not self._currentAction:self._actions.pop(0)I worry about this getting stuck. Nothing concrete, but maybe we should break or throw an error if _actions is empty?
I don't know that this is practically possible, but I suppose theoretically it is.
I can just add to both places, like is happening in _on_continue_clicked right now, something like this:
if not self._actions: sys.exit(0) return
I don't know what else we could possibly do.
- Chris
--- po/POTFILES.in | 1 + 1 file changed, 1 insertion(+)
diff --git a/po/POTFILES.in b/po/POTFILES.in index 0009665..e58edf5 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -7,6 +7,7 @@ pyanaconda/bootloader.py pyanaconda/cmdline.py pyanaconda/constants.py pyanaconda/image.py +pyanaconda/install.py pyanaconda/installclass.py pyanaconda/installinterfacebase.py pyanaconda/iutil.py
anaconda-patches@lists.fedorahosted.org