ubuntu-22.04.3-desktop-amd64/casper/filesystem/usr/lib/ubiquity/plugins/ubi-language.py

789 lines
31 KiB
Python

# -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-
# Copyright (C) 2006, 2007, 2008 Canonical Ltd.
# Written by Colin Watson <cjwatson@ubuntu.com>.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty 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 St, Fifth Floor, Boston, MA 02110-1301 USA
import locale
import os
import re
import debconf
from ubiquity import auto_update, i18n, misc, osextras, plugin
NAME = 'language'
AFTER = None
WEIGHT = 10
try:
import lsb_release
_ver = lsb_release.get_distro_information()['RELEASE']
except Exception:
_ver = '12.04'
_wget_url = 'http://changelogs.ubuntu.com/ubiquity/%s-update-available' % _ver
_release_notes_url_path = '/cdrom/.disk/release_notes_url'
class PageBase(plugin.PluginUI):
def set_language_choices(self, unused_choices, choice_map):
"""Called with language choices and a map to localised names."""
self.language_choice_map = dict(choice_map)
def set_language(self, language):
"""Set the current selected language."""
pass
def get_language(self):
"""Get the current selected language."""
return 'C'
def set_oem_id(self, text):
pass
def get_oem_id(self):
return ''
class PageGtk(PageBase):
plugin_is_language = True
plugin_title = 'ubiquity/text/language_heading_label'
def __init__(self, controller, *args, **kwargs):
self.controller = controller
self.timeout_id = None
self.wget_retcode = None
self.wget_proc = None
if self.controller.oem_user_config:
ui_file = 'stepLanguageOnly.ui'
self.only = True
else:
ui_file = 'stepLanguage.ui'
self.only = False
from gi.repository import Gtk
builder = Gtk.Builder()
builder.add_from_file(os.path.join(
os.environ['UBIQUITY_GLADE'], ui_file))
builder.connect_signals(self)
self.controller.add_builder(builder)
self.page = builder.get_object('stepLanguage')
self.iconview = builder.get_object('language_iconview')
self.treeview = builder.get_object('language_treeview')
self.oem_id_entry = builder.get_object('oem_id_entry')
if self.controller.oem_config:
builder.get_object('oem_id_vbox').show()
self.release_notes_url = ''
self.update_installer = True
self.updating_installer = False
self.release_notes_label = builder.get_object('release_notes_label')
self.release_notes_found = False
if self.release_notes_label:
self.release_notes_label.connect(
'activate-link', self.on_link_clicked)
if self.controller.oem_config or auto_update.already_updated():
self.update_installer = False
try:
with open(_release_notes_url_path) as release_notes:
self.release_notes_url = release_notes.read().rstrip('\n')
self.release_notes_found = True
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
self.install_ubuntu = builder.get_object('install_ubuntu')
self.try_ubuntu = builder.get_object('try_ubuntu')
if not self.only:
if 'UBIQUITY_GREETER' not in os.environ:
choice_section_vbox = builder.get_object('choice_section_vbox')
choice_section_vbox and choice_section_vbox.hide()
else:
def inst(*args):
self.try_ubuntu.set_sensitive(False)
self.controller.go_forward()
self.install_ubuntu.connect('clicked', inst)
self.try_ubuntu.connect('clicked', self.on_try_ubuntu_clicked)
self.try_install_text_label = builder.get_object(
'try_install_text_label')
# We do not want to show the yet to be substituted strings
# (${MEDIUM}, etc), so don't show the core of the page until
# it's ready.
for w in self.page.get_children():
w.hide()
self.plugin_widgets = self.page
@plugin.only_this_page
def on_try_ubuntu_clicked(self, *args):
if not self.controller.allowed_change_step():
# The button's already been clicked once, so stop reacting to it.
# LP: #911907.
return
# Spinning cursor.
self.controller.allow_change_step(False)
# Queue quit.
self.install_ubuntu.set_sensitive(False)
self.controller._wizard.current_page = None
self.controller.dbfilter.ok_handler()
def set_language_choices(self, choices, choice_map):
from gi.repository import Gtk, GObject
PageBase.set_language_choices(self, choices, choice_map)
list_store = Gtk.ListStore.new([GObject.TYPE_STRING])
longest_length = 0
longest = ''
for choice in choices:
list_store.append([choice])
# Work around the fact that GtkIconView wraps at 50px or the width
# of the icon, which is nonexistent here. See adjust_wrap_width ()
# in gtkiconview.c for the details.
if self.only:
length = len(choice)
if length > longest_length:
longest_length = length
longest = choice
# Support both iconview and treeview
if self.only:
self.iconview.set_model(list_store)
self.iconview.set_text_column(0)
pad = self.iconview.get_property('item-padding')
layout = self.iconview.create_pango_layout(longest)
self.iconview.set_item_width(layout.get_pixel_size()[0] + pad * 2)
else:
if len(self.treeview.get_columns()) < 1:
column = Gtk.TreeViewColumn(
None, Gtk.CellRendererText(), text=0)
column.set_sizing(Gtk.TreeViewColumnSizing.GROW_ONLY)
self.treeview.append_column(column)
selection = self.treeview.get_selection()
selection.connect(
'changed', self.on_language_selection_changed)
self.treeview.set_model(list_store)
def set_language(self, language):
# Support both iconview and treeview
if self.only:
model = self.iconview.get_model()
iterator = model.iter_children(None)
while iterator is not None:
if misc.utf8(model.get_value(iterator, 0)) == language:
path = model.get_path(iterator)
self.iconview.select_path(path)
self.iconview.scroll_to_path(path, True, 0.5, 0.5)
break
iterator = model.iter_next(iterator)
else:
model = self.treeview.get_model()
iterator = model.iter_children(None)
while iterator is not None:
if misc.utf8(model.get_value(iterator, 0)) == language:
path = model.get_path(iterator)
self.treeview.get_selection().select_path(path)
self.treeview.scroll_to_cell(
path, use_align=True, row_align=0.5)
break
iterator = model.iter_next(iterator)
if not self.only and 'UBIQUITY_GREETER' in os.environ:
self.try_ubuntu.set_sensitive(True)
self.install_ubuntu.set_sensitive(True)
def get_language(self):
# Support both iconview and treeview
if self.only:
model = self.iconview.get_model()
items = self.iconview.get_selected_items()
if not items:
return None
iterator = model.get_iter(items[0])
else:
selection = self.treeview.get_selection()
(model, iterator) = selection.get_selected()
if iterator is None:
return None
else:
value = misc.utf8(model.get_value(iterator, 0))
return self.language_choice_map[value][1]
def on_language_activated(self, *args, **kwargs):
self.controller.go_forward()
def on_language_selection_changed(self, *args, **kwargs):
lang = self.get_language()
self.controller.allow_go_forward(bool(lang))
if not lang:
return
if 'UBIQUITY_GREETER' in os.environ:
misc.set_indicator_keymaps(lang)
# strip encoding; we use UTF-8 internally no matter what
lang = lang.split('.')[0]
self.controller.translate(lang)
from gi.repository import Gtk
ltr = i18n.get_string('default-ltr', lang, 'ubiquity/imported')
if ltr == 'default:RTL':
Gtk.Widget.set_default_direction(Gtk.TextDirection.RTL)
else:
Gtk.Widget.set_default_direction(Gtk.TextDirection.LTR)
if self.only:
# The language page for oem-config doesn't have the fancy greeter.
return
# TODO: Cache these.
release = misc.get_release()
install_medium = misc.get_install_medium()
install_medium = i18n.get_string(install_medium, lang)
# Set the release name (Ubuntu 10.04) and medium (USB or CD) where
# necessary.
w = self.try_install_text_label
text = i18n.get_string(Gtk.Buildable.get_name(w), lang)
text = text.replace('${RELEASE}', release.name)
text = text.replace('${MEDIUM}', install_medium)
w.set_label(text)
# Big buttons.
for w in (self.try_ubuntu, self.install_ubuntu):
text = i18n.get_string(Gtk.Buildable.get_name(w), lang)
text = text.replace('${RELEASE}', release.name)
text = text.replace('${MEDIUM}', install_medium)
w.get_child().set_markup('<span size="x-large">%s</span>' % text)
# We need to center each button under each image *and* have a homogeous
# size between the two buttons.
self.try_ubuntu.set_size_request(-1, -1)
self.install_ubuntu.set_size_request(-1, -1)
try_w = self.try_ubuntu.size_request().width
install_w = self.install_ubuntu.size_request().width
if try_w > install_w:
self.try_ubuntu.set_size_request(try_w, -1)
self.install_ubuntu.set_size_request(try_w, -1)
elif install_w > try_w:
self.try_ubuntu.set_size_request(install_w, -1)
self.install_ubuntu.set_size_request(install_w, -1)
# Make the forward button a consistent size, regardless of its text.
install_label = i18n.get_string('install_button', lang)
next_button = self.controller._wizard.next
next_label = next_button.get_label()
next_button.set_size_request(-1, -1)
next_w = next_button.size_request().width
next_button.set_label(install_label)
install_w = next_button.size_request().width
next_button.set_label(next_label)
if next_w > install_w:
next_button.set_size_request(next_w, -1)
else:
next_button.set_size_request(install_w, -1)
self.update_release_notes_label()
for w in self.page.get_children():
w.show()
def plugin_set_online_state(self, state):
from gi.repository import GLib
if self.release_notes_label:
if self.timeout_id:
GLib.source_remove(self.timeout_id)
if state:
self.release_notes_label.show()
self.timeout_id = GLib.timeout_add(
300, self.check_returncode)
else:
self.release_notes_label.hide()
def check_returncode(self, *args):
import subprocess
if self.wget_retcode is not None or self.wget_proc is None:
self.wget_proc = subprocess.Popen(
['wget', '-q', _wget_url, '--timeout=15', '--tries=1',
'-O', '/dev/null'])
self.wget_retcode = self.wget_proc.poll()
if self.wget_retcode is None:
return True
else:
if self.wget_retcode == 0:
self.update_installer = True
else:
self.update_installer = False
self.update_release_notes_label()
return False
def update_release_notes_label(self):
print("update_release_notes_label()")
lang = self.get_language()
if not lang:
return
# strip encoding; we use UTF-8 internally no matter what
lang = lang.split('.')[0]
# Either leave the release notes label alone (both release notes and a
# critical update are available), set it to just the release notes,
# just the critical update, or neither, as appropriate.
if self.release_notes_label:
if self.release_notes_found and self.update_installer:
text = i18n.get_string('release_notes_label', lang)
self.release_notes_label.set_markup(text)
elif self.release_notes_found:
text = i18n.get_string('release_notes_only', lang)
self.release_notes_label.set_markup(text)
elif self.update_installer:
text = i18n.get_string('update_installer_only', lang)
self.release_notes_label.set_markup(text)
else:
self.release_notes_label.set_markup('')
def set_oem_id(self, text):
return self.oem_id_entry.set_text(text)
def get_oem_id(self):
return self.oem_id_entry.get_text()
def on_link_clicked(self, widget, uri):
# Connected in glade.
lang = self.get_language()
if not lang:
lang = 'C'
lang = lang.split('.')[0] # strip encoding
if uri == 'update':
if self.updating_installer:
return True
self.updating_installer = True
if not auto_update.update(self.controller._wizard):
# no updates, so don't check again
if self.release_notes_url:
text = i18n.get_string('release_notes_only', lang)
self.release_notes_label.set_markup(text)
else:
self.release_notes_label.hide()
self.updating_installer = False
elif uri == 'release-notes':
uri = self.release_notes_url.replace('${LANG}', lang)
misc.launch_uri(uri)
return True
class PageKde(PageBase):
plugin_breadcrumb = 'ubiquity/text/breadcrumb_language'
plugin_is_language = True
def __init__(self, controller, *args, **kwargs):
self.controller = controller
self.wget_retcode = None
self.wget_proc = None
if self.controller.oem_user_config:
self.only = True
else:
self.only = False
try:
from PyQt5 import uic
from PyQt5.QtGui import QPixmap, QIcon
from PyQt5.QtWidgets import QWidget
self.page = uic.loadUi('/usr/share/ubiquity/qt/stepLanguage.ui')
self.combobox = self.page.language_combobox
# Tell layout that all items are of uniform sizes
# Fixes LP:1187762
self.combobox.view().setUniformItemSizes(True)
self.combobox.currentIndexChanged[str].connect(
self.on_language_selection_changed)
if not self.controller.oem_config:
self.page.oem_widget.hide()
def init_big_button(button, image_name):
pix = QPixmap('/usr/share/ubiquity/qt/images/' + image_name)
icon = QIcon(pix)
button.setIcon(icon)
button.setIconSize(pix.size())
# Set a fixed height to ensure the button text is not cropped
# when the window is resized
button.setFixedHeight(button.sizeHint().height())
def inst(*args):
self.page.try_ubuntu.setEnabled(False)
self.controller.go_forward()
self.page.install_ubuntu.clicked.connect(inst)
self.page.try_ubuntu.clicked.connect(self.on_try_ubuntu_clicked)
init_big_button(self.page.install_ubuntu, 'install.png')
init_big_button(self.page.try_ubuntu, 'try.png')
self.release_notes_url = ''
self.update_installer = True
self.updating_installer = False
if self.controller.oem_config or auto_update.already_updated():
self.update_installer = False
self.release_notes_found = False
try:
with open(_release_notes_url_path) as release_notes:
self.release_notes_url = release_notes.read().rstrip('\n')
self.release_notes_found = True
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
if self.release_notes_url:
self.page.release_notes_label.linkActivated.connect(
self.on_release_notes_link)
else:
self.page.release_notes_label.hide()
if 'UBIQUITY_GREETER' not in os.environ:
self.page.try_ubuntu.hide()
self.page.try_install_text_label.hide()
self.page.install_ubuntu.hide()
# We do not want to show the yet to be substituted strings
# (${MEDIUM}, etc), so don't show the core of the page until
# it's ready.
self.widgetHidden = []
for w in self.page.children():
if isinstance(w, QWidget) and not w.isHidden():
self.widgetHidden.append(w)
w.hide()
except Exception as e:
self.debug('Could not create language page: %s', e)
self.page = None
self.plugin_widgets = self.page
@plugin.only_this_page
def on_try_ubuntu_clicked(self, *args):
if not self.controller.allowed_change_step():
# The button's already been clicked once, so stop reacting to it.
# LP: #911907.
return
# Spinning cursor.
self.controller.allow_change_step(False)
# Queue quit.
self.page.install_ubuntu.setEnabled(False)
self.controller._wizard.current_page = None
self.controller.dbfilter.ok_handler()
def on_release_notes_link(self, link):
lang = self.selected_language()
if link == "release-notes":
if lang:
lang = lang.split('.')[0].lower()
url = self.release_notes_url.replace('${LANG}', lang)
self.openURL(url)
elif link == "update":
if self.updating_installer:
return
self.updating_installer = True
if not auto_update.update(self.controller._wizard):
# no updates, so don't check again
text = i18n.get_string('release_notes_only', lang)
self.page.release_notes_label.setText(text)
self.updating_installer = False
def openURL(self, url):
from PyQt5.QtGui import QDesktopServices
from PyQt5.QtCore import QUrl
import shutil
import os
# this nonsense is needed because kde doesn't want to be root
misc.drop_privileges()
misc.drop_privileges_save()
# copy over gtkrc-2.0 to get the themeing right
if os.path.exists("/usr/share/kubuntu-default-settings"):
shutil.copy("/usr/share/kubuntu-default-settings/" +
"dot-gtkrc-2.0-kde4",
os.getenv("HOME") + "/.gtkrc-2.0")
QDesktopServices.openUrl(QUrl(url))
misc.regain_privileges()
misc.regain_privileges_save()
def set_language_choices(self, choices, choice_map):
PageBase.set_language_choices(self, choices, choice_map)
self.combobox.clear()
self.combobox.addItems(choices)
def set_language(self, language):
index = self.combobox.findText(str(language))
if index < 0:
self.combobox.addItem("C")
else:
self.combobox.setCurrentIndex(index)
if not self.only and 'UBIQUITY_GREETER' in os.environ:
self.page.try_ubuntu.setEnabled(True)
self.page.install_ubuntu.setEnabled(True)
def get_language(self):
lang = self.selected_language()
return lang if lang else 'C'
def selected_language(self):
lang = self.combobox.currentText()
if not lang or not hasattr(self, 'language_choice_map'):
return None
else:
return self.language_choice_map[str(lang)][1]
def on_language_selection_changed(self):
lang = self.selected_language()
if not lang:
return
# strip encoding; we use UTF-8 internally no matter what
lang = lang.split('.')[0]
self.controller.translate(lang)
if not self.only:
release = misc.get_release()
install_medium = misc.get_install_medium()
install_medium = i18n.get_string(install_medium, lang)
for widget in (self.page.try_install_text_label,
self.page.try_ubuntu,
self.page.install_ubuntu):
text = widget.text()
text = text.replace('${RELEASE}', release.name)
text = text.replace('${MEDIUM}', install_medium)
text = text.replace('Ubuntu', 'Kubuntu')
widget.setText(text)
self.update_release_notes_label()
for w in self.widgetHidden:
w.show()
self.widgetHidden = []
def plugin_set_online_state(self, state):
from PyQt5.QtCore import QTimer
if self.page.release_notes_label:
if state:
self.page.release_notes_label.show()
QTimer.singleShot(300, self.check_returncode)
self.timer = QTimer(self.page)
self.timer.timeout.connect(self.check_returncode)
self.timer.start(300)
else:
self.page.release_notes_label.hide()
def check_returncode(self, *args):
import subprocess
if self.wget_retcode is not None or self.wget_proc is None:
self.wget_proc = subprocess.Popen(
['wget', '-q', _wget_url, '--timeout=15', '--tries=1',
'-O', '/dev/null'])
self.wget_retcode = self.wget_proc.poll()
if self.wget_retcode is None:
return True
else:
if self.wget_retcode == 0:
self.update_installer = True
else:
self.update_installer = False
self.update_release_notes_label()
self.timer.timeout.disconnect(self.check_returncode)
def update_release_notes_label(self):
lang = self.selected_language()
if not lang:
return
# strip encoding; we use UTF-8 internally no matter what
lang = lang.split('.')[0]
# Either leave the release notes label alone (both release notes and a
# critical update are available), set it to just the release notes,
# just the critical update, or neither, as appropriate.
if self.page.release_notes_label:
if self.release_notes_found and self.update_installer:
text = i18n.get_string('release_notes_label', lang)
self.page.release_notes_label.setText(text)
elif self.release_notes_found:
text = i18n.get_string('release_notes_only', lang)
self.page.release_notes_label.setText(text)
elif self.update_installer:
text = i18n.get_string('update_installer_only', lang)
self.page.release_notes_label.setText(text)
def set_oem_id(self, text):
return self.page.oem_id_entry.setText(text)
def get_oem_id(self):
return str(self.page.oem_id_entry.text())
class PageDebconf(PageBase):
plugin_title = 'ubiquity/text/language_heading_label'
def __init__(self, controller, *args, **kwargs):
self.controller = controller
class PageNoninteractive(PageBase):
def __init__(self, controller, *args, **kwargs):
self.controller = controller
def set_language(self, language):
"""Set the current selected language."""
# Use the language code, not the translated name
self.language = self.language_choice_map[language][1]
def get_language(self):
"""Get the current selected language."""
return self.language
class Page(plugin.Plugin):
def prepare(self, unfiltered=False):
self.language_question = None
self.initial_language = None
self.db.fset('localechooser/languagelist', 'seen', 'false')
with misc.raised_privileges():
osextras.unlink_force('/var/lib/localechooser/preseeded')
osextras.unlink_force('/var/lib/localechooser/langlevel')
if self.ui.controller.oem_config:
try:
self.ui.set_oem_id(self.db.get('oem-config/id'))
except debconf.DebconfError:
pass
localechooser_script = '/usr/lib/ubiquity/localechooser/localechooser'
if ('UBIQUITY_FRONTEND' in os.environ and
os.environ['UBIQUITY_FRONTEND'] == 'debconf_ui'):
localechooser_script += '-debconf'
questions = ['localechooser/languagelist']
environ = {
'PATH': '/usr/lib/ubiquity/localechooser:' + os.environ['PATH'],
}
if ('UBIQUITY_FRONTEND' in os.environ and
os.environ['UBIQUITY_FRONTEND'] == "debconf_ui"):
environ['TERM_FRAMEBUFFER'] = '1'
else:
environ['OVERRIDE_SHOW_ALL_LANGUAGES'] = '1'
return localechooser_script, questions, environ
def run(self, priority, question):
if question == 'localechooser/languagelist':
self.language_question = question
if self.initial_language is None:
self.initial_language = self.db.get(question)
current_language_index = self.value_index(question)
only_installable = misc.create_bool(
self.db.get('ubiquity/only-show-installable-languages'))
current_language, sorted_choices, language_display_map = \
i18n.get_languages(current_language_index, only_installable)
self.ui.set_language_choices(sorted_choices,
language_display_map)
self.ui.set_language(current_language)
if len(sorted_choices) == 1:
self.done = True
return True
return plugin.Plugin.run(self, priority, question)
def cancel_handler(self):
# Undo effects of UI translation.
self.ui.controller.translate(just_me=False, not_me=True)
plugin.Plugin.cancel_handler(self)
def ok_handler(self):
if self.language_question is not None:
new_language = self.ui.get_language()
self.preseed(self.language_question, new_language)
if (self.initial_language is None or
self.initial_language != new_language):
self.db.reset('debian-installer/country')
if self.ui.controller.oem_config:
self.preseed('oem-config/id', self.ui.get_oem_id())
plugin.Plugin.ok_handler(self)
def cleanup(self):
plugin.Plugin.cleanup(self)
i18n.reset_locale(self.frontend)
self.frontend.stop_debconf()
self.ui.controller.translate(just_me=False, not_me=True, reget=True)
class Install(plugin.InstallPlugin):
def prepare(self, unfiltered=False):
if 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
command = ['/usr/share/ubiquity/localechooser-apply']
else:
command = [
'sh', '-c',
'/usr/lib/ubiquity/localechooser/post-base-installer ' +
'&& /usr/lib/ubiquity/localechooser/finish-install',
]
return command, []
def _pam_env_lines(self, fd):
"""Yield a sequence of logical lines from a PAM environment file."""
buf = ""
for line in fd:
line = line.lstrip()
if line and line[0] != "#":
if "#" in line:
line = line[:line.index("#")]
for i in range(len(line) - 1, -1, -1):
if line[i].isspace():
break
if line[i] == "\\":
# Continuation line.
buf = line[:i]
else:
buf += line
yield buf
buf = ""
if buf:
yield buf
def _pam_env_parse_file(self, path):
"""Parse a PAM environment file just as pam_env does.
We use this for reading /etc/default/locale after configuring it.
"""
try:
with open(path) as fd:
for line in self._pam_env_lines(fd):
line = line.lstrip()
if not line or line[0] == "#":
continue
if line.startswith("export "):
line = line[7:]
line = re.sub(r"[#\n].*", "", line)
if not re.match(r"^[A-Z_]+=", line):
continue
# pam_env's handling of quoting is crazy, but in this
# case it's better to imitate it than to fix it.
key, value = line.split("=", 1)
if value.startswith('"') or value.startswith("'"):
value = re.sub(r"[\"'](.|$)", r"\1", value[1:])
yield key, value
except IOError:
pass
def install(self, target, progress, *args, **kwargs):
progress.info('ubiquity/install/locales')
rv = plugin.InstallPlugin.install(
self, target, progress, *args, **kwargs)
if not rv:
locale_file = "/etc/default/locale"
if 'UBIQUITY_OEM_USER_CONFIG' not in os.environ:
locale_file = "/target%s" % locale_file
for key, value in self._pam_env_parse_file(locale_file):
if key in ("LANG", "LANGUAGE") or key.startswith("LC_"):
os.environ[key] = value
# Start using the newly-generated locale, if possible.
try:
locale.setlocale(locale.LC_ALL, '')
except locale.Error:
pass
return rv