Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1872 lines (1612 sloc) 70.6 KB
# -*- coding: utf-8; Mode: Python; indent-tabs-mode: nil; tab-width: 4 -*-
#
# «gtk_ui» - GTK user interface
#
# Copyright (C) 2005 Junta de Andalucía
# Copyright (C) 2005, 2006, 2007, 2008, 2009 Canonical Ltd.
#
# Authors:
#
# - Javier Carranza <javier.carranza#interactors._coop>
# - Juan Jesús Ojeda Croissier <juanje#interactors._coop>
# - Antonio Olmo Titos <aolmo#emergya._info>
# - Gumer Coronel Pérez <gcoronel#emergya._info>
# - Colin Watson <cjwatson@ubuntu.com>
# - Evan Dandrea <ev@ubuntu.com>
# - Mario Limonciello <superm1@ubuntu.com>
#
# This file is part of Ubiquity.
#
# Ubiquity 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.
#
# Ubiquity 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 Ubiquity; if not, write to the Free Software Foundation, Inc., 51
# Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from __future__ import print_function
import atexit
import configparser
from functools import reduce
import gettext
import gi
import os
import subprocess
import sys
import syslog
import traceback
import dbus
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
# in query mode we won't be in X, but import needs to pass
if 'DISPLAY' in os.environ:
from gi.repository import Gtk, Gdk, GObject, GLib, Atk
from ubiquity import gtkwidgets
from ubiquity import (
filteredcommand, gsettings, i18n, validation, misc, osextras)
from ubiquity.components import install, plugininstall, partman_commit
import ubiquity.frontend.base
from ubiquity.frontend.base import BaseFrontend
from ubiquity.plugin import Plugin
import ubiquity.progressposition
# We create class attributes dynamically from UI files, and it's far too
# tedious to list them all.
__pychecker__ = 'no-classattr'
# Define global path
PATH = os.environ.get('UBIQUITY_PATH', False) or '/usr/share/ubiquity'
# Define ui path
UIDIR = os.environ.get('UBIQUITY_GLADE', False) or os.path.join(PATH, 'gtk')
os.environ['UBIQUITY_GLADE'] = UIDIR
# Define locale path
LOCALEDIR = "/usr/share/locale"
def set_root_cursor(cursor=None):
if cursor is None:
cursor = Gdk.Cursor.new(Gdk.CursorType.ARROW)
win = Gdk.get_default_root_window()
if win:
win.set_cursor(cursor)
gtkwidgets.refresh()
class Controller(ubiquity.frontend.base.Controller):
def add_builder(self, builder):
self._wizard.builders.append(builder)
def translate(self, lang=None, just_me=True, not_me=False, reget=False):
if lang:
self._wizard.locale = lang
self._wizard.translate_pages(lang, just_me, not_me, reget)
def allow_go_forward(self, allowed):
try:
self._wizard.allow_go_forward(allowed)
except AttributeError:
pass
def allow_go_backward(self, allowed):
try:
self._wizard.allow_go_backward(allowed)
except AttributeError:
pass
def allow_change_step(self, allowed):
try:
self._wizard.allow_change_step(allowed)
except AttributeError:
pass
def allowed_change_step(self):
return self._wizard.allowed_change_step
def go_forward(self):
self._wizard.next.activate()
def go_backward(self):
self._wizard.back.activate()
def go_to_page(self, widget):
self._wizard.set_current_page(self._wizard.steps.page_num(widget))
def toggle_top_level(self):
if self._wizard.live_installer.get_property('visible'):
self._wizard.live_installer.hide()
else:
self._wizard.live_installer.show()
self._wizard.refresh()
def toggle_progress_section(self):
if self._wizard.progress_section.get_property('visible'):
self._wizard.progress_section.hide()
else:
self._wizard.progress_section.show()
self._wizard.refresh()
def get_string(self, name, lang=None, prefix=None):
return self._wizard.get_string(name, lang, prefix)
def toggle_navigation_control(self, hideFlag):
if hideFlag:
self._wizard.navigation_control.show()
else:
self._wizard.navigation_control.hide()
self._wizard.refresh()
def toggle_next_button(self, label='gtk-go-forward'):
self._wizard.toggle_next_button(label)
def toggle_skip_button(self, label='skip'):
self._wizard.toggle_skip_button(label)
def switch_to_install_interface(self):
self._wizard.switch_to_install_interface()
class Wizard(BaseFrontend):
def __init__(self, distro):
def add_subpage(self, steps, name):
"""Inserts a subpage into the notebook. This assumes the file
shares the same base name as the page you are looking for."""
widget = None
uifile = UIDIR + '/' + name + '.ui'
if os.path.exists(uifile):
self.builder.add_from_file(uifile)
widget = self.builder.get_object(name)
steps.append_page(widget, None)
else:
print('Could not find ui file %s' % name, file=sys.stderr)
return widget
def add_widget(self, widget):
"""Make a widget callable by the toplevel."""
if not isinstance(widget, Gtk.Widget):
return
name = Gtk.Buildable.get_name(widget)
widget.set_name(name)
if 'UBIQUITY_LDTP' in os.environ:
atk_desc = widget.get_accessible()
atk_desc.set_name(name)
self.all_widgets.add(widget)
setattr(self, widget.get_name(), widget)
# We generally want labels to be selectable so that people can
# easily report problems in them
# (https://launchpad.net/bugs/41618), but GTK+ likes to put
# selectable labels in the focus chain, and I can't seem to turn
# this off in glade and have it stick. Accordingly, make sure
# labels are unfocusable here.
label = None
if isinstance(widget, Gtk.Label):
label = widget
elif isinstance(widget, gtkwidgets.StateBox):
label = widget.label
if label:
label.set_selectable(True)
label.set_property('can-focus', False)
BaseFrontend.__init__(self, distro)
self.previous_excepthook = sys.excepthook
sys.excepthook = self.excepthook
# declare attributes
self.all_widgets = set()
self.gsettings_previous = {}
self.thunar_previous = {}
self.language_questions = ('live_installer', 'quit', 'back', 'next',
'warning_dialog', 'warning_dialog_label',
'cancelbutton', 'exitbutton',
'install_button', 'restart_to_continue')
self.current_page = None
self.backup = None
self.allowed_change_step = True
self.allowed_go_backward = True
self.allowed_go_forward = True
self.stay_on_page = False
self.progress_position = ubiquity.progressposition.ProgressPosition()
self.progress_cancelled = False
self.installing = False
self.installing_no_return = False
self.returncode = 0
self.history = []
self.builder = Gtk.Builder()
self.grub_options = Gtk.ListStore(
GObject.TYPE_STRING, GObject.TYPE_STRING)
self.finished_installing = False
self.finished_pages = False
self.parallel_db = None
self.timeout_id = None
self.screen_reader = False
self.orca_process = None
# To get a "busy mouse":
self.watch = Gdk.Cursor.new(Gdk.CursorType.WATCH)
self.set_busy_cursor(True)
atexit.register(set_root_cursor)
# Are we running alongside Orca?
with open('/proc/cmdline') as fp:
if 'access=v3' in fp.read():
self.screen_reader = True
# set default language
self.locale = i18n.reset_locale(self)
# set custom language
self.set_locales()
# Get the default window background color for the the current
# theme and set it as the background for the inline toolbar
# Make a thin Progress bar
provider = Gtk.CssProvider()
provider.load_from_data(b'''\
toolbar {
background: @theme_bg_color;
border-color: transparent;
border-width: 0px;
padding: 0px;
}
progressbar trough {
min-height: 10px;
min-width: 11px;
border: none;
}
progressbar progress {
min-height: 10px;
border-radius: 4px;
border: none;
}
paned separator {
min-width: 10px;
}
''')
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
# load the main interface
self.builder.add_from_file('%s/ubiquity.ui' % UIDIR)
self.builders = [self.builder]
self.pages = []
self.pagesindex = 0
self.pageslen = 0
steps = self.builder.get_object("steps")
found_install = False
for mod in self.modules:
if hasattr(mod.module, 'PageGtk'):
mod.ui_class = mod.module.PageGtk
mod.controller = Controller(self)
mod.ui = mod.ui_class(mod.controller)
mod.title = mod.ui.get('plugin_title')
widgets = mod.ui.get('plugin_widgets')
optional_widgets = mod.ui.get('plugin_optional_widgets')
if not found_install:
found_install = mod.ui.get('plugin_is_install')
if widgets or optional_widgets:
def fill_out(widget_list):
rv = []
if not isinstance(widget_list, list):
widget_list = [widget_list]
for w in widget_list:
if not w:
continue
if isinstance(w, str):
w = add_subpage(self, steps, w)
else:
steps.append_page(w, None)
rv.append(w)
return rv
mod.widgets = fill_out(widgets)
mod.optional_widgets = fill_out(optional_widgets)
mod.all_widgets = mod.widgets + mod.optional_widgets
self.pageslen += 1
self.pages.append(mod)
# If no plugins declare they are install, then we'll say the last one
# is
if not found_install:
self.pages[self.pageslen - 1].ui.plugin_is_install = True
self.toplevels = set()
for builder in self.builders:
for widget in builder.get_objects():
add_widget(self, widget)
if isinstance(widget, Gtk.Window):
self.toplevels.add(widget)
self.builder.connect_signals(self)
for mod in self.pages:
progress = Gtk.ProgressBar()
progress.get_accessible().set_role(Atk.Role.INVALID)
progress.set_size_request(10, 10)
progress.set_fraction(0)
self.dot_grid.add(progress)
next_style = self.next.get_style_context()
next_style.add_class('ubiquity-next')
self.progress_pages = {
'empty': 0,
'dot_grid': 1,
'progress_bar': 2,
}
self.stop_debconf()
self.translate_widgets(reget=True)
self.customize_installer()
# Put up the a11y indicator.
if osextras.find_on_path('a11y-profile-manager-indicator'):
try:
subprocess.Popen(['a11y-profile-manager-indicator',
'-i'], preexec_fn=misc.drop_all_privileges)
except Exception:
print("Unable to set up accessibility profile support",
file=sys.stderr)
self.live_installer.connect(
'key-press-event', self.a11y_profile_keys)
if osextras.find_on_path('canberra-gtk-play'):
subprocess.Popen(
['canberra-gtk-play', '--id=system-ready'],
preexec_fn=misc.drop_all_privileges)
def all_children(self, parent):
if isinstance(parent, Gtk.Container):
def recurse(x, y):
return x + self.all_children(y)
rv = reduce(recurse, parent.get_children(), [parent])
return rv
else:
return [parent]
def translate_pages(self, lang=None, just_current=True, not_current=False,
reget=False):
current_page = self.pages[self.pagesindex]
if just_current:
pages = [current_page]
else:
pages = self.pages
if reget:
self.translate_reget(lang)
widgets = []
for p in pages:
# There's no sense retranslating the page we're leaving.
if not_current and p == current_page:
continue
prefix = p.ui.get('plugin_prefix')
for w in p.all_widgets:
for c in self.all_children(w):
widgets.append((c, prefix))
if not just_current:
for toplevel in self.toplevels:
if toplevel.get_name() != 'live_installer':
for c in self.all_children(toplevel):
widgets.append((c, None))
self.translate_widgets(lang=lang, widgets=widgets, reget=False)
self.set_page_title(current_page, lang)
# Allow plugins to provide a hook for translation.
for p in pages:
# There's no sense retranslating the page we're leaving.
if not_current and p == current_page:
continue
if hasattr(p.ui, 'plugin_translate'):
try:
p.ui.plugin_translate(lang or self.locale)
except Exception as e:
print('Could not translate page (%s): %s' %
(p.module.NAME, str(e)), file=sys.stderr)
def excepthook(self, exctype, excvalue, exctb):
"""Crash handler."""
if (issubclass(exctype, KeyboardInterrupt) or
issubclass(exctype, SystemExit)):
return
# Restore the default cursor if we were using a spinning cursor on the
# root window.
try:
self.set_busy_cursor(False)
except Exception:
pass
tbtext = ''.join(traceback.format_exception(exctype, excvalue, exctb))
syslog.syslog(syslog.LOG_ERR,
"Exception in GTK frontend (invoking crash handler):")
for line in tbtext.split('\n'):
syslog.syslog(syslog.LOG_ERR, line)
print("Exception in GTK frontend (invoking crash handler):",
file=sys.stderr)
print(tbtext, file=sys.stderr)
self.post_mortem(exctype, excvalue, exctb)
if os.path.exists('/usr/share/apport/apport-gtk'):
self.previous_excepthook(exctype, excvalue, exctb)
# In live session mode, update-notifier will pick up the crash
# report; in only-ubiquity mode we need to bring up the UI
# ourselves
# update-notifier doesn't work on overlayfs so also run if using
# maybe-ubiquity
# FIXME: Revert the check to maybe-ubiquity once inotify on
# overlayfs is fixed (crash will then be detected by
# update-notifier)
with open('/proc/cmdline') as fp:
if 'ubiquity' in fp.read():
# we need to drop privileges, we cannot run GTK programs
# with non-matching real/effective u/gid
misc.drop_all_privileges()
misc.execute('/usr/share/apport/apport-gtk')
sys.exit(1)
else:
self.crash_detail_label.set_text(tbtext)
self.crash_dialog.run()
self.crash_dialog.hide()
self.live_installer.hide()
self.refresh()
misc.execute_root("apport-bug", "ubiquity")
sys.exit(1)
def network_change(self, online=False):
if not online:
self.set_online_state(False)
return
if self.timeout_id:
GLib.source_remove(self.timeout_id)
self.timeout_id = GLib.timeout_add(300, self.check_returncode)
def set_online_state(self, state):
for p in self.pages:
if hasattr(p.ui, 'plugin_set_online_state'):
p.ui.plugin_set_online_state(state)
def thunar_set_volmanrc(self, fields):
previous = {}
if 'SUDO_USER' in os.environ:
thunar_dir = os.path.expanduser('~%s/.config/Thunar' %
os.environ['SUDO_USER'])
else:
thunar_dir = os.path.expanduser('~/.config/Thunar')
if os.path.isdir(thunar_dir):
thunar_volmanrc = '%s/volmanrc' % thunar_dir
parser = configparser.RawConfigParser()
parser.optionxform = str # case-sensitive
parser.read(thunar_volmanrc)
if not parser.has_section('Configuration'):
parser.add_section('Configuration')
for key, value in fields.items():
if parser.has_option('Configuration', key):
previous[key] = parser.get('Configuration', key)
else:
previous[key] = 'TRUE'
parser.set('Configuration', key, value)
try:
with open('%s.new' % thunar_volmanrc,
'w') as thunar_volmanrc_new:
parser.write(thunar_volmanrc_new)
os.rename('%s.new' % thunar_volmanrc, thunar_volmanrc)
except (KeyboardInterrupt, SystemExit):
raise
except Exception:
pass
return previous
def disable_terminal(self):
gs_schema = 'org.gnome.settings-daemon.plugins.media-keys'
gs_key = 'terminal'
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
return
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value:
gsettings.set(gs_schema, gs_key, '')
atexit.register(self.enable_terminal)
def enable_terminal(self):
gs_schema = 'org.gnome.settings-daemon.plugins.media-keys'
gs_key = 'terminal'
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
def disable_screen_reader(self):
gs_key = 'screenreader'
for gs_schema in 'org.gnome.settings-daemon.plugins.media-keys', \
'org.mate.SettingsDaemon.plugins.media-keys':
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
return
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value:
gsettings.set(gs_schema, gs_key, '')
atexit.register(self.enable_screen_reader)
def enable_screen_reader(self):
gs_key = 'screenreader'
for gs_schema in 'org.gnome.settings-daemon.plugins.media-keys', \
'org.mate.SettingsDaemon.plugins.media-keys':
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
def disable_screensaver(self):
gs_schema = 'org.gnome.desktop.screensaver'
gs_key = 'idle-activation-enabled'
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
return
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value:
gsettings.set(gs_schema, gs_key, False)
atexit.register(self.enable_screensaver)
def enable_screensaver(self):
gs_schema = 'org.gnome.desktop.screensaver'
gs_key = 'idle-activation-enabled'
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
def disable_powermgr(self):
gs_schema = 'org.gnome.settings-daemon.plugins.power'
gs_key = 'active'
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
return
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value:
gsettings.set(gs_schema, gs_key, False)
atexit.register(self.enable_powermgr)
def enable_powermgr(self):
gs_schema = 'org.gnome.settings-daemon.plugins.power'
gs_key = 'active'
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
def disable_logout_indicator(self):
gs_schema = 'com.canonical.indicator.session'
gs_key = 'suppress-logout-menuitem'
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
return
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value:
gsettings.set(gs_schema, gs_key, True)
atexit.register(self.enable_logout_indicator)
def enable_logout_indicator(self):
gs_schema = 'com.canonical.indicator.session'
gs_key = 'suppress-logout-menuitem'
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
# Disable gnome-volume-manager automounting to avoid problems during
# partitioning.
def disable_volume_manager(self):
volumes_visible = (
'org.gnome.nautilus.desktop', 'volumes-visible', False)
media_automount = (
'org.gnome.desktop.media-handling', 'automount', False)
media_automount_open = (
'org.gnome.desktop.media-handling', 'automount-open', False)
media_autorun_never = (
'org.gnome.desktop.media-handling', 'autorun-never', True)
for keys in (volumes_visible, media_automount, media_automount_open,
media_autorun_never):
gs_schema = keys[0]
gs_key = keys[1]
gs_wantedvalue = keys[2]
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
continue
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
if gs_value != gs_wantedvalue:
gsettings.set(gs_schema, gs_key, gs_wantedvalue)
self.thunar_previous = self.thunar_set_volmanrc(
{'AutomountDrives': 'FALSE', 'AutomountMedia': 'FALSE'})
atexit.register(self.enable_volume_manager)
def enable_volume_manager(self):
volumes_visible = ('org.gnome.nautilus.desktop', 'volumes-visible')
media_automount = ('org.gnome.desktop.media-handling', 'automount')
media_automount_open = (
'org.gnome.desktop.media-handling', 'automount-open')
media_autorun_never = (
'org.gnome.desktop.media-handling', 'autorun-never')
for keys in (volumes_visible, media_automount, media_automount_open,
media_autorun_never):
gs_schema = keys[0]
gs_key = keys[1]
gs_previous = '%s/%s' % (gs_schema, gs_key)
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
if self.thunar_previous:
self.thunar_set_volmanrc(self.thunar_previous)
def a11y_profile_keys(self, window, event):
if osextras.find_on_path('a11y-profile-manager'):
hc_profile_found = False
sr_profile_found = False
subp = subprocess.Popen(['a11y-profile-manager',
'-l'],
stdout=subprocess.PIPE,
preexec_fn=misc.drop_all_privileges,
universal_newlines=True)
for line in subp.stdout:
value = line.rstrip('\n')
if value.endswith('high-contrast'):
hc_profile_found = True
self.hc_profile_name = value
if value.endswith('blindness'):
sr_profile_found = True
self.sr_profile_name = value
if (hc_profile_found is True and event.state &
Gdk.ModifierType.CONTROL_MASK and
event.keyval == Gdk.keyval_from_name('h')):
self.a11y_profile_high_contrast_activate()
elif (sr_profile_found is True and event.state &
Gdk.ModifierType.SUPER_MASK and
event.state & Gdk.ModifierType.MOD1_MASK and
event.keyval == Gdk.keyval_from_name('s')):
self.a11y_profile_screen_reader_activate()
def a11y_profile_set(self, value):
gsettings.set("com.canonical.a11y-profile-manager",
"active-profile", value)
def a11y_profile_high_contrast_activate(self):
self.a11y_profile_set(self.hc_profile_name)
def a11y_profile_screen_reader_activate(self, widget=None):
self.a11y_profile_set(self.sr_profile_name)
os.environ['UBIQUITY_A11Y_PROFILE'] = 'screen-reader'
def run(self):
"""run the interface."""
if os.getuid() != 0:
title = ('This installer must be run with administrative '
'privileges, and cannot continue without them.')
dialog = Gtk.MessageDialog(
self.live_installer, Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE, title)
dialog.run()
sys.exit(1)
self.disable_volume_manager()
self.disable_screensaver()
self.disable_powermgr()
self.disable_screen_reader()
if 'UBIQUITY_ONLY' in os.environ:
self.disable_logout_indicator()
if 'UBIQUITY_DEBUG' not in os.environ:
self.disable_terminal()
# show interface
self.allow_change_step(True)
# Auto-connecting signals with additional parameters does not work.
self.grub_new_device_entry.connect(
'changed', self.grub_verify_loop, self.grub_fail_okbutton)
if 'UBIQUITY_AUTOMATIC' in os.environ:
self.debconf_progress_start(
0, self.pageslen, self.get_string('ubiquity/install/checking'))
self.debconf_progress_cancellable(False)
self.refresh()
self.set_current_page(0)
self.live_installer.show()
while(self.pagesindex < len(self.pages)):
if self.current_page is None:
return self.returncode
page = self.pages[self.pagesindex]
skip = False
if hasattr(page.ui, 'plugin_skip_page'):
if page.ui.plugin_skip_page():
skip = True
automatic = False
if hasattr(page.ui, 'is_automatic'):
automatic = page.ui.is_automatic
if not skip and not page.filter_class:
# This page is just a UI page
self.dbfilter = None
self.dbfilter_status = None
if self.set_page(page.module.NAME):
self.run_main_loop()
elif not skip:
old_dbfilter = self.dbfilter
if issubclass(page.filter_class, Plugin):
ui = page.ui
else:
ui = None
self.start_debconf()
self.dbfilter = page.filter_class(self, ui=ui)
if self.dbfilter is not None and self.dbfilter != old_dbfilter:
self.allow_change_step(False)
GLib.idle_add(
lambda: self.dbfilter.start(auto_process=True))
page.controller.dbfilter = self.dbfilter
Gtk.main()
self.pending_quits = max(0, self.pending_quits - 1)
page.controller.dbfilter = None
if self.backup or self.dbfilter_handle_status():
if self.current_page is not None and not self.backup:
self.process_step()
if not self.stay_on_page:
self.pagesindex = self.pagesindex + 1
if automatic:
# if no debconf_progress, create another one, set
# start to pageindex
self.debconf_progress_step(1)
self.refresh()
if self.backup:
self.pagesindex = self.pop_history()
self.refresh()
# There's still work to do (postinstall). Let's keep the user
# entertained.
self.start_slideshow()
Gtk.main()
self.pending_quits = max(0, self.pending_quits - 1)
# postinstall will exit here by calling Gtk.main_quit in
# find_next_step.
self.unlock_environment()
if self.oem_user_config:
self.quit_installer()
elif not (self.get_reboot_seen() or self.get_shutdown_seen()):
self.live_installer.hide()
if ('UBIQUITY_ONLY' in os.environ or
'UBIQUITY_GREETER' in os.environ):
txt = self.get_string('ubiquity/finished_restart_only')
self.quit_button.hide()
else:
txt = self.finished_label.get_label()
txt = txt.replace('${RELEASE}', misc.get_release().name)
self.finished_label.set_label(txt)
with misc.raised_privileges():
with open('/var/run/reboot-required', "w"):
pass
self.finished_dialog.set_keep_above(True)
self.set_busy_cursor(False)
self.finished_dialog.run()
elif self.get_reboot():
self.reboot()
elif self.get_shutdown():
self.shutdown()
return self.returncode
def on_context_menu(self, unused_web_view, unused_context_menu,
unused_event, unused_hit_test_result):
# True will not show the menu
return True
def on_slideshow_link_clicked(self, web_view, decision, decision_type):
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2
if decision_type == WebKit2.PolicyDecisionType.NEW_WINDOW_ACTION:
request = decision.get_request()
uri = request.get_uri()
decision.ignore()
subprocess.Popen(['sensible-browser', uri],
close_fds=True,
preexec_fn=misc.drop_all_privileges)
return True
return False
def start_slideshow(self):
# WebKit2 spawns a process which we don't want to run as root
misc.drop_privileges_save()
self.progress_mode.set_current_page(
self.progress_pages['progress_bar'])
if not self.slideshow:
self.page_mode.hide()
return
self.page_section.hide()
slideshow_locale = self.slideshow_get_available_locale(
self.slideshow, self.locale)
slideshow_main = os.path.join(self.slideshow, 'slides', 'index.html')
parameters = []
parameters.append('locale=%s' % slideshow_locale)
ltr = i18n.get_string(
'default-ltr', slideshow_locale, 'ubiquity/imported')
if ltr == 'default:RTL':
parameters.append('rtl')
parameters_encoded = '&'.join(parameters)
slides = 'file://%s#%s' % (slideshow_main, parameters_encoded)
gi.require_version('WebKit2', '4.0')
from gi.repository import WebKit2
# We have no significant browsing interface, so there isn't much point
# in WebKit creating a memory-hungry cache.
context = WebKit2.WebContext.get_default()
context.set_cache_model(WebKit2.CacheModel.DOCUMENT_VIEWER)
webview = WebKit2.WebView()
# WebKit puts file URLs in their own domain by default.
# This means that anything which checks for the same origin,
# such as creating a XMLHttpRequest, will fail unless this
# is disabled.
# http://www.gitorious.org/webkit/webkit/commit/624b946
s = webview.get_settings()
s.set_property('allow-file-access-from-file-urls', True)
webview.connect('context-menu', self.on_context_menu)
if (os.environ.get('UBIQUITY_A11Y_PROFILE') == 'screen-reader'):
s.set_property('enable-caret-browsing', True)
webview.connect('decide-policy',
self.on_slideshow_link_clicked)
webview.show()
self.page_mode.insert_page(webview, None, 1)
webview.load_uri(slides)
# TODO do these in a page loaded callback
self.page_mode.show()
self.page_mode.set_current_page(1)
webview.grab_focus()
misc.regain_privileges_save()
def customize_installer(self):
"""Initial UI setup."""
self.live_installer.set_icon_name('ubiquity')
for eventbox in ['title_eventbox', 'progress_eventbox',
'install_details_expander']:
box = self.builder.get_object(eventbox)
style = box.get_style_context()
style.add_class('ubiquity-menubar')
# TODO lazy load
import gi
gi.require_version("Vte", "2.91")
from gi.repository import Vte, Pango
misc.drop_privileges_save()
self.vte = Vte.Terminal()
self.install_details_sw.add(self.vte)
tail_cmd = [
'/bin/busybox', 'tail', '-f', '/var/log/installer/debug',
'-f', '/var/log/syslog', '-q',
]
self.vte.spawn_sync(0, None, tail_cmd, None, 0, None, None, None)
fontdesc = Pango.font_description_from_string("Ubuntu Mono 8")
self.vte.set_font(fontdesc)
self.vte.show()
misc.regain_privileges_save()
# FIXME shrink the window horizontally instead of locking the window
# size.
self.live_installer.set_resizable(False)
def expand(widget):
if widget.get_property('expanded'):
self.progress_cancel_button.show()
else:
self.progress_cancel_button.hide()
self.install_details_expander.connect_after('activate', expand)
if self.custom_title:
self.live_installer.set_title(self.custom_title)
elif self.oem_config:
self.live_installer.set_title(self.get_string('oem_config_title'))
elif self.oem_user_config:
self.live_installer.set_title(
self.get_string('oem_user_config_title'))
self.live_installer.set_icon_name("preferences-system")
self.quit.hide()
self.back.hide()
self.progress_section.show()
if 'UBIQUITY_AUTOMATIC' in os.environ:
# Hide the notebook until the first page is ready.
self.page_mode.hide()
self.progress_mode.set_current_page(
self.progress_pages['progress_bar'])
self.live_installer.show()
else:
self.progress_mode.set_current_page(
self.progress_pages['dot_grid'])
self.progress_mode.show_all()
self.allow_change_step(False)
# The default instantiation of GtkComboBoxEntry creates a
# GtkCellRenderer, so reuse it.
self.grub_new_device_entry.set_model(self.grub_options)
self.grub_new_device_entry.set_entry_text_column(0)
renderer = Gtk.CellRendererText()
self.grub_new_device_entry.pack_start(renderer, True)
self.grub_new_device_entry.add_attribute(renderer, 'text', 1)
# Only show the Shutdown Now button if explicitly asked to.
if not self.show_shutdown_button:
self.shutdown_button.hide()
# Parse the slideshow size early to prevent the window from growing
if (self.oem_user_config and
os.path.exists('/usr/share/oem-config-slideshow')):
self.slideshow = '/usr/share/oem-config-slideshow'
else:
self.slideshow = '/usr/share/ubiquity-slideshow'
if os.path.exists(self.slideshow) and not self.hide_slideshow:
try:
cfg = configparser.ConfigParser()
cfg.read(os.path.join(self.slideshow, 'slideshow.conf'))
config_width = int(cfg.get('Slideshow', 'width'))
config_height = int(cfg.get('Slideshow', 'height'))
except Exception:
config_width = 752
config_height = 442
self.webkit_scrolled_window.set_size_request(
config_width, config_height)
else:
self.slideshow = None
# set initial bottom bar status
self.allow_go_backward(False)
misc.add_connection_watch(self.network_change)
def set_window_hints(self, widget):
if (self.oem_user_config or
'UBIQUITY_ONLY' in os.environ or
'UBIQUITY_GREETER' in os.environ):
f = (Gdk.WMFunction.RESIZE | Gdk.WMFunction.MAXIMIZE |
Gdk.WMFunction.MOVE)
if not self.oem_user_config:
f |= Gdk.WMFunction.CLOSE
widget.get_window().set_functions(f)
def lockdown_environment(self):
atexit.register(self.unlock_environment)
keys = (
('com.canonical.indicator.session', 'suppress-logout-menuitem'),
('com.canonical.indicator.session',
'suppress-logout-restart-shutdown'),
('com.canonical.indicator.session', 'suppress-restart-menuitem'),
('com.canonical.indicator.session', 'suppress-shutdown-menuitem'),
('org.gnome.desktop.lockdown', 'disable-user-switching'),
)
for key in keys:
gs_schema = key[0]
gs_key = key[1]
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
continue
gs_value = gsettings.get(gs_schema, gs_key)
self.gsettings_previous[gs_previous] = gs_value
gsettings.set(gs_schema, gs_key, True)
self.quit.hide()
f = (Gdk.WMFunction.RESIZE | Gdk.WMFunction.MAXIMIZE |
Gdk.WMFunction.MOVE)
if 'UBIQUITY_ONLY' not in os.environ:
f |= Gdk.WMFunction.MINIMIZE
self.live_installer.get_window().set_functions(f)
self.allow_change_step(False)
self.refresh()
def unlock_environment(self):
syslog.syslog('Reverting lockdown of the desktop environment.')
keys = (
('com.canonical.indicator.session', 'suppress-logout-menuitem'),
('com.canonical.indicator.session',
'suppress-logout-restart-shutdown'),
('com.canonical.indicator.session', 'suppress-restart-menuitem'),
('com.canonical.indicator.session', 'suppress-shutdown-menuitem'),
('org.gnome.desktop.lockdown', 'disable-user-switching'),
)
for key in keys:
gs_schema = key[0]
gs_key = key[1]
gs_previous = '%s/%s' % (gs_schema, gs_key)
if gs_previous in self.gsettings_previous:
gs_value = self.gsettings_previous[gs_previous]
gsettings.set(gs_schema, gs_key, gs_value)
if not self.oem_user_config:
self.quit.show()
f = Gdk.WMFunction.RESIZE | Gdk.WMFunction.MAXIMIZE | \
Gdk.WMFunction.MOVE | Gdk.WMFunction.CLOSE
if 'UBIQUITY_ONLY' not in os.environ:
f |= Gdk.WMFunction.MINIMIZE
self.refresh()
def set_locales(self):
"""internationalization config. Use only once."""
domain = self.distro + '-installer'
gettext.bindtextdomain(domain, LOCALEDIR)
self.builder.set_translation_domain(domain)
gettext.textdomain(domain)
gettext.install(domain, LOCALEDIR)
def translate_reget(self, lang):
if lang is None:
lang = self.locale
if lang is None:
languages = []
else:
languages = [lang]
core_names = ['ubiquity/text/%s' % q for q in self.language_questions]
core_names.append('ubiquity/text/oem_config_title')
core_names.append('ubiquity/text/oem_user_config_title')
core_names.append('ubiquity/imported/default-ltr')
core_names.append('ubiquity/text/release_notes_only')
core_names.append('ubiquity/text/update_installer_only')
core_names.append('ubiquity/text/USB')
core_names.append('ubiquity/text/CD')
stock_items = (
'cancel', 'close', 'go-back', 'go-forward', 'ok', 'quit')
for stock_item in stock_items:
core_names.append('ubiquity/imported/%s' % stock_item)
prefixes = []
for p in self.pages:
prefix = p.ui.get('plugin_prefix')
if not prefix:
prefix = 'ubiquity/text'
if p.ui.get('plugin_is_language'):
children = reduce(
lambda x, y: x + self.all_children(y), p.all_widgets, [])
core_names.extend(
[prefix + '/' + c.get_name() for c in children])
title = p.ui.get('plugin_title')
if title:
core_names.extend([title])
prefixes.append(prefix)
i18n.get_translations(
languages=languages, core_names=core_names,
extra_prefixes=prefixes)
# widgets is a set of (widget, prefix) pairs
def translate_widgets(self, lang=None, widgets=None, reget=True):
if lang is None:
lang = self.locale
if widgets is None:
widgets = [(x, None) for x in self.all_widgets]
if reget:
self.translate_reget(lang)
# We always translate always-visible widgets
for q in self.language_questions:
if hasattr(self, q):
widgets.append((getattr(self, q), None))
for widget in widgets:
self.translate_widget(widget[0], lang=lang, prefix=widget[1])
def translate_widget(self, widget, lang=None, prefix=None):
if isinstance(widget, Gtk.Button) and widget.get_use_stock():
widget.set_label(widget.get_label())
text = self.get_string(widget.get_name(), lang, prefix)
if text is None:
return
name = widget.get_name()
if isinstance(widget, Gtk.Label):
widget.set_markup(text)
elif isinstance(widget, Gtk.Button):
question = i18n.map_widget_name(prefix, widget.get_name())
widget.set_label(text)
# Workaround for radio button labels disappearing on second
# translate when not visible. LP: #353090
widget.realize()
if question.startswith('ubiquity/imported/'):
stock_id = question[18:]
widget.set_use_stock(False)
widget.set_image(Gtk.Image.new_from_stock(
'gtk-%s' % stock_id, Gtk.IconSize.BUTTON))
elif isinstance(widget, Gtk.Window):
if name == 'live_installer':
if self.custom_title:
text = self.custom_title
elif self.oem_config:
text = self.get_string('oem_config_title', lang)
elif self.oem_user_config:
text = self.get_string('oem_user_config_title', lang)
widget.set_title(text)
elif isinstance(widget, Gtk.MenuItem):
widget.set_label(text)
def set_busy_cursor(self, busy):
if busy:
cursor = self.watch
else:
cursor = None
if (hasattr(self, "live_installer") and
self.live_installer.get_parent_window()):
self.live_installer.get_parent_window().set_cursor(cursor)
set_root_cursor(cursor)
self.busy_cursor = busy
def allow_change_step(self, allowed):
self.set_busy_cursor(not allowed)
self.back.set_sensitive(allowed and self.allowed_go_backward)
self.next.set_sensitive(allowed and self.allowed_go_forward)
self.allowed_change_step = allowed
def allow_go_backward(self, allowed):
self.back.set_sensitive(allowed and self.allowed_change_step)
self.allowed_go_backward = allowed
def allow_go_forward(self, allowed):
self.next.set_sensitive(allowed and self.allowed_change_step)
self.allowed_go_forward = allowed
def dbfilter_handle_status(self):
"""If a dbfilter crashed, ask the user if they want to continue anyway.
Returns True to continue, or False to try again."""
if not self.dbfilter_status or self.current_page is None:
return True
syslog.syslog('dbfilter_handle_status: %s' % str(self.dbfilter_status))
# TODO cjwatson 2007-04-04: i18n
text = ('%s failed with exit code %s. Further information may be '
'found in /var/log/syslog. Do you want to try running this '
'step again before continuing? If you do not, your '
'installation may fail entirely or may be broken.' %
(self.dbfilter_status[0], self.dbfilter_status[1]))
dialog = Gtk.Dialog('%s crashed' % self.dbfilter_status[0],
self.live_installer, Gtk.DialogFlags.MODAL,
(Gtk.STOCK_QUIT, Gtk.ResponseType.CLOSE,
'Continue anyway', 1,
'Try again', 2))
self.dbfilter_status = None
label = Gtk.Label(label=text)
label.set_line_wrap(True)
label.set_selectable(False)
dialog.get_content_area().add(label)
dialog.show_all()
response = dialog.run()
dialog.hide()
syslog.syslog('dbfilter_handle_status: response %d' % response)
if response == 1:
return True
elif response == Gtk.ResponseType.CLOSE:
self.quit_installer()
else:
step = self.step_name(self.steps.get_current_page())
if step == "partman":
print('dbfilter_handle_status stepPart')
self.set_current_page(self.steps.page_num(self.stepPartAuto))
return False
def step_name(self, step_index, return_index=False):
w = self.steps.get_nth_page(step_index)
name = None
index = None
for p in self.pages:
if w in p.all_widgets:
name = p.module.NAME
index = self.pages.index(p)
if return_index:
return name, index
return name
def page_name(self, step_index):
return self.steps.get_nth_page(step_index).get_name()
def toggle_next_button(self, label='gtk-go-forward'):
if label != 'gtk-go-forward':
self.next.set_label(self.get_string(label))
else:
self.next.set_label(label)
self.translate_widget(self.next)
def toggle_skip_button(self, label='skip'):
self.skip.set_label(self.get_string(label))
self.skip.show()
def set_page(self, n):
self.run_automation_error_cmd()
# We only stop the backup process when we're on a page where questions
# need to be asked, otherwise you wont be able to back up past
# pages that do not stop on questions or are preseeded away.
self.backup = False
visible = self.live_installer.get_property('visible')
self.live_installer.show()
# Work around a bug in the wrap_fix code whereby the layout does not
# get properly rendered due to the window not being visible.
if not visible:
self.live_installer.resize_children()
self.page_mode.show()
cur = None
is_install = False
if 'UBIQUITY_GREETER' in os.environ:
for page in self.pages:
if page.module.NAME == 'language':
# The greeter page is quite large. Hide it upon leaving.
page.ui.page.hide()
break
for page in self.pages:
if page.module.NAME == n:
# Now ask ui class which page we want to be showing right now
if hasattr(page.ui, 'plugin_get_current_page'):
cur = page.ui.call('plugin_get_current_page')
if isinstance(cur, str) and hasattr(self, cur):
cur = getattr(self, cur) # for not-yet-plugins
elif page.widgets:
cur = page.widgets[0]
elif page.optional_widgets:
cur = page.optional_widgets[0]
if cur:
self.set_page_title(page)
self.skip.set_visible(
hasattr(page.ui, 'plugin_on_skip_clicked'))
cur.show()
is_install = page.ui.get('plugin_is_install')
break
if not cur:
return False
if is_install and not self.oem_user_config:
self.toggle_next_button('install_button')
else:
self.toggle_next_button()
num = self.steps.page_num(cur)
if num < 0:
print('Invalid page found for %s: %s' % (n, str(cur)),
file=sys.stderr)
return False
self.add_history(page, cur)
self.set_current_page(num)
if self.pagesindex == 0:
self.allow_go_backward(False)
elif self.pages[self.pagesindex - 1].module.NAME == 'partman':
# We're past partitioning. Unless the install fails, there is no
# going back.
self.allow_go_backward(False)
elif 'UBIQUITY_AUTOMATIC' not in os.environ:
self.allow_go_backward(True)
# If we are in oem-config, ensure the back button is displayed if
# and only if we are not on the first page.
if self.oem_user_config:
self.back.set_visible(self.pagesindex > 0)
return True
def set_page_title(self, page, lang=None):
"""Fetches and/or retranslates a page title"""
title = None
if page.title:
title = self.get_string(page.title, lang)
if title:
title = title.replace('${RELEASE}', misc.get_release().name)
# TODO: Use attributes instead? Would save having to
# hardcode the size in here.
self.page_title.set_markup(
'<span size="xx-large">%s</span>' % title)
self.title_section.show()
if not page.title or not title:
self.title_section.hide()
def set_focus(self):
# Make sure that something reasonable has the focus. If the first
# focusable item is a label or a button (often, the welcome text label
# and the quit button), set the focus to the next button.
if not self.live_installer.get_focus():
self.live_installer.child_focus(Gtk.DirectionType.TAB_FORWARD)
focus = self.live_installer.get_focus()
if focus:
if focus.__class__ == Gtk.Label:
# When it got focus, the whole text was selected.
focus.select_region(-1, -1)
self.next.grab_focus()
elif focus.__class__ == Gtk.Button:
self.next.grab_focus()
return True
def set_current_page(self, current):
if self.steps.get_current_page() == current:
# self.steps.set_current_page() will do nothing. Update state
# ourselves.
self.on_steps_switch_page(
self.steps, self.steps.get_nth_page(current), current)
else:
self.steps.set_current_page(current)
# Methods
def reboot(self, *args):
"""Reboot the system after installing."""
self.returncode = 10
self.quit_installer()
def shutdown(self, *args):
"""Shutdown the system after installing."""
self.returncode = 11
self.quit_installer()
def do_reboot(self):
"""Callback for main program to actually reboot the machine."""
try:
session = dbus.Bus.get_session()
gnome_session = session.name_has_owner('org.gnome.SessionManager')
except dbus.exceptions.DBusException:
gnome_session = False
if gnome_session:
manager = session.get_object('org.gnome.SessionManager',
'/org/gnome/SessionManager')
manager.RequestReboot()
else:
# don't let reboot race with the shutdown of X in ubiquity-dm;
# reboot might be too fast and X will stay around forever instead
# of moving to plymouth
misc.execute_root(
"sh", "-c",
"if ! service display-manager status; then killall Xorg; "
"while pidof X; do sleep 0.5; done; fi; reboot")
def do_shutdown(self):
"""Callback for main program to actually shutdown the machine."""
try:
session = dbus.Bus.get_session()
gnome_session = session.name_has_owner('org.gnome.SessionManager')
except dbus.exceptions.DBusException:
gnome_session = False
if gnome_session:
manager = session.get_object('org.gnome.SessionManager',
'/org/gnome/SessionManager')
manager.RequestShutdown()
else:
# don't let poweroff race with the shutdown of X in ubiquity-dm;
# poweroff might be too fast and X will stay around forever instead
# of moving to plymouth
misc.execute_root(
"sh", "-c",
"if ! service display-manager status; then killall Xorg; "
"while pidof X; do sleep 0.5; done; fi; poweroff")
def quit_installer(self, *args):
"""Quit installer cleanly."""
# Let the user know we're shutting down.
self.finished_dialog.get_window().set_cursor(self.watch)
self.set_busy_cursor(True)
self.quit_button.set_sensitive(False)
self.reboot_button.set_sensitive(False)
self.refresh()
# exiting from application
self.current_page = None
self.warning_dialog.hide()
if self.dbfilter is not None:
self.dbfilter.cancel_handler()
self.quit_main_loop()
# Callbacks
def dialog_hide_on_delete(self, widget, event, data=None):
widget.hide()
return True
def on_quit_clicked(self, unused_widget):
self.warning_dialog.set_transient_for(
self.live_installer.get_toplevel())
self.warning_dialog.show_all()
# Stop processing.
return True
def on_quit_cancelled(self, unused_widget):
self.warning_dialog.hide()
def on_live_installer_delete_event(self, widget, unused_event):
return self.on_quit_clicked(widget)
def on_skip_clicked(self, unused_widget):
ui = self.pages[self.pagesindex].ui
if hasattr(ui, 'plugin_on_skip_clicked'):
ui.plugin_on_skip_clicked()
def on_next_clicked(self, unused_widget):
"""Callback to control the installation process between steps."""
if not self.allowed_change_step or not self.allowed_go_forward:
return
self.allow_change_step(False)
ui = self.pages[self.pagesindex].ui
if hasattr(ui, 'plugin_on_next_clicked'):
if ui.plugin_on_next_clicked():
# Stop processing and return to the page.
self.allow_change_step(True)
return
if self.dbfilter is not None:
self.dbfilter.ok_handler()
# expect recursive main loops to be exited and
# debconffilter_done() to be called when the filter exits
else:
self.find_next_step(self.pages[self.pagesindex].module.__name__)
self.quit_main_loop()
def process_step(self):
"""Process and validate the results of this step."""
# setting actual step
step_num = self.steps.get_current_page()
step = self.page_name(step_num)
syslog.syslog('Step_before = %s' % step)
def on_back_clicked(self, unused_widget):
"""Callback to set previous screen."""
if not self.allowed_change_step:
return
self.allow_change_step(False)
ui = self.pages[self.pagesindex].ui
if hasattr(ui, 'plugin_on_back_clicked'):
if ui.plugin_on_back_clicked():
# Stop processing and return to the page.
self.allow_change_step(True)
return
self.backup = True
self.stay_on_page = False
# Enabling next button
self.allow_go_forward(True)
if self.dbfilter is not None:
self.dbfilter.cancel_handler()
# expect recursive main loops to be exited and
# debconffilter_done() to be called when the filter exits
else:
self.find_next_step(self.pages[self.pagesindex].module.__name__)
self.quit_main_loop()
def on_steps_switch_page(self, unused_notebook, unused_page, current):
self.current_page = current
name, index = self.step_name(current, return_index=True)
if 'UBIQUITY_GREETER' in os.environ:
if name == 'language':
self.navigation_control.hide()
else:
self.navigation_control.show()
for i in range(len(self.pages))[:index + 1]:
self.dot_grid.get_child_at(i, 0).set_fraction(1)
for i in range(len(self.pages))[index + 1:]:
self.dot_grid.get_child_at(i, 0).set_fraction(0)
syslog.syslog('switched to page %s' % name)
# Callbacks provided to components.
def watch_debconf_fd(self, from_debconf, process_input):
GLib.io_add_watch(from_debconf,
GLib.IO_IN | GLib.IO_ERR | GLib.IO_HUP,
self.watch_debconf_fd_helper, process_input)
def watch_debconf_fd_helper(self, source, cb_condition, callback):
debconf_condition = 0
if (cb_condition & GLib.IO_IN) != 0:
debconf_condition |= filteredcommand.DEBCONF_IO_IN
if (cb_condition & GLib.IO_ERR) != 0:
debconf_condition |= filteredcommand.DEBCONF_IO_ERR
if (cb_condition & GLib.IO_HUP) != 0:
debconf_condition |= filteredcommand.DEBCONF_IO_HUP
return callback(source, debconf_condition)
def debconf_progress_start(self, progress_min, progress_max,
progress_title):
self.progress_position.start(progress_min, progress_max,
progress_title)
self.debconf_progress_set(0)
self.debconf_progress_info(progress_title)
def debconf_progress_set(self, progress_val):
if self.progress_cancelled:
return False
self.progress_position.set(progress_val)
fraction = self.progress_position.fraction()
self.install_progress.set_fraction(fraction)
return True
def debconf_progress_step(self, progress_inc):
if self.progress_cancelled:
return False
self.progress_position.step(progress_inc)
fraction = self.progress_position.fraction()
self.install_progress.set_fraction(fraction)
return True
def debconf_progress_info(self, progress_info):
if self.progress_cancelled:
return False
self.install_progress_text.set_label(progress_info)
return True
def debconf_progress_stop(self):
self.progress_cancelled = False
self.progress_position.stop()
def debconf_progress_region(self, region_start, region_end):
self.progress_position.set_region(region_start, region_end)
def debconf_progress_cancellable(self, cancellable):
if cancellable:
self.progress_cancel_button.set_sensitive(True)
else:
self.progress_cancel_button.set_sensitive(False)
self.progress_cancelled = False
def on_progress_cancel_button_clicked(self, unused_button):
self.progress_cancelled = True
def debconffilter_done(self, dbfilter):
if not dbfilter.status:
self.find_next_step(dbfilter.__module__)
# TODO: This doesn't handle partman-commit failures.
elif dbfilter.__module__ in ('ubiquity.components.install',
'ubiquity.components.plugininstall'):
# We don't want to try to retry a failing step here, because it
# will have the same set of inputs, and thus likely the same
# result.
# TODO: We may want to call return_to_partitioning after the crash
# dialog instead.
self.crash_dialog.run()
self.crash_dialog.hide()
self.live_installer.hide()
self.refresh()
misc.execute_root("apport-bug", "ubiquity")
sys.exit(1)
if BaseFrontend.debconffilter_done(self, dbfilter):
self.quit_main_loop()
return True
else:
return False
def switch_to_install_interface(self):
self.installing = True
self.lockdown_environment()
def find_next_step(self, finished_step):
# TODO need to handle the case where debconffilters launched from
# here crash. Factor code out of dbfilter_handle_status.
last_page = self.pages[-1].module.__name__
if finished_step == last_page and not self.backup:
self.finished_pages = True
if self.finished_installing or self.oem_user_config:
self.debconf_progress_info('')
# thaw container size
self.progress_section.set_size_request(-1, -1)
self.install_details_expander.show()
self.install_progress.show()
self.progress_section.show()
dbfilter = plugininstall.Install(self)
dbfilter.start(auto_process=True)
elif finished_step == 'ubi-partman':
# Flush changes to the database so that when the parallel db
# starts, it does so with the most recent changes.
self.stop_debconf()
self.start_debconf()
options = misc.grub_options()
self.grub_options.clear()
for opt in options:
self.grub_options.append(opt)
self.switch_to_install_interface()
from ubiquity.debconfcommunicator import DebconfCommunicator
if self.parallel_db is not None:
# Partitioning failed and we're coming back through again.
self.parallel_db.shutdown()
env = os.environ.copy()
# debconf-apt-progress, start_debconf()
env['DEBCONF_DB_REPLACE'] = 'configdb'
env['DEBCONF_DB_OVERRIDE'] = 'Pipe{infd:none outfd:none}'
self.parallel_db = DebconfCommunicator('ubiquity', cloexec=True,
env=env)
dbfilter = partman_commit.PartmanCommit(self, db=self.parallel_db)
dbfilter.start(auto_process=True)
# FIXME OH DEAR LORD. Use isinstance.
elif finished_step == 'ubiquity.components.partman_commit':
dbfilter = install.Install(self, db=self.parallel_db)
dbfilter.start(auto_process=True)
elif finished_step == 'ubiquity.components.install':
self.finished_installing = True
if self.finished_pages:
dbfilter = plugininstall.Install(self)
dbfilter.start(auto_process=True)
else:
# temporarily freeze container size
allocation = self.progress_section.get_allocation()
self.progress_section.set_size_request(
allocation.width, allocation.height)
self.install_details_expander.hide()
self.install_progress.hide()
elif finished_step == 'ubiquity.components.plugininstall':
self.installing = False
self.run_success_cmd()
self.quit_main_loop()
def grub_verify_loop(self, widget, okbutton):
if widget is not None:
if validation.check_grub_device(widget.get_child().get_text()):
okbutton.set_sensitive(True)
else:
okbutton.set_sensitive(False)
def return_to_partitioning(self):
"""If the install progress bar is up but still at the partitioning
stage, then errors can safely return us to partitioning.
"""
self.page_section.show()
if self.installing and not self.installing_no_return:
# Stop the currently displayed page.
if self.dbfilter is not None:
self.dbfilter.cancel_handler()
# Go back to the partitioner and try again.
self.pagesindex = -1
for page in self.pages:
if page.module.NAME == 'partman':
self.pagesindex = self.pages.index(page)
break
if self.pagesindex == -1:
return
self.start_debconf()
ui = self.pages[self.pagesindex].ui
self.dbfilter = self.pages[self.pagesindex].filter_class(
self, ui=ui)
self.allow_change_step(False)
self.dbfilter.start(auto_process=True)
self.toggle_next_button()
self.translate_widget(self.next)
self.installing = False
self.progress_section.hide()
self.unlock_environment()
def error_dialog(self, title, msg, fatal=True):
# TODO: cancel button as well if capb backup
self.run_automation_error_cmd()
saved_busy_cursor = self.busy_cursor
self.set_busy_cursor(False)
if not msg:
msg = title
dialog = Gtk.MessageDialog(
self.live_installer, Gtk.DialogFlags.MODAL,
Gtk.MessageType.ERROR, Gtk.ButtonsType.OK, msg)
dialog.set_title(title)
dialog.run()
self.set_busy_cursor(saved_busy_cursor)
dialog.hide()
if fatal:
self.return_to_partitioning()
def toggle_grub_fail(self, unused_widget):
if self.grub_no_new_device.get_active():
self.no_grub_warn.show()
self.grub_new_device_entry.set_sensitive(False)
self.abort_warn.hide()
elif self.grub_fail_option.get_active():
self.abort_warn.show()
self.no_grub_warn.hide()
self.grub_new_device_entry.set_sensitive(False)
else:
self.abort_warn.hide()
self.no_grub_warn.hide()
self.grub_new_device_entry.set_sensitive(True)
def bootloader_dialog(self, current_device):
ret = self.skip_label.get_label()
ret = ret.replace('${RELEASE}', misc.get_release().name)
self.skip_label.set_label(ret)
self.grub_new_device_entry.get_child().set_text(current_device)
self.grub_new_device_entry.get_child().grab_focus()
response = self.bootloader_fail_dialog.run()
self.bootloader_fail_dialog.hide()
if response == Gtk.ResponseType.OK:
if self.grub_new_device.get_active():
return self.grub_new_device_entry.get_child().get_text()
elif self.grub_no_new_device.get_active():
return 'skip'
else:
return ''
else:
return ''
def question_dialog(self, title, msg, options, use_templates=True):
self.run_automation_error_cmd()
saved_busy_cursor = self.busy_cursor
self.set_busy_cursor(False)
if not msg:
msg = title
buttons = []
for option in options:
if use_templates:
text = self.get_string(option)
else:
text = option
if text is None:
text = option
buttons.append((text, len(buttons) + 1))
self.ubi_question_dialog.set_title(title)
self.question_label.set_text(msg)
actions = self.ubi_question_dialog.get_action_area()
for action in actions.get_children():
actions.remove(action)
for text, response_id in buttons:
self.ubi_question_dialog.add_button(text, response_id)
self.ubi_question_dialog.show_all()
response = self.ubi_question_dialog.run()
self.set_busy_cursor(saved_busy_cursor)
self.ubi_question_dialog.hide()
if response < 0:
# something other than a button press, probably destroyed
return None
else:
return options[response - 1]
def refresh(self):
gtkwidgets.refresh()
def run_main_loop(self):
"""Run the UI's main loop until it returns control to us."""
self.allow_change_step(True)
self.set_focus()
Gtk.main()
self.pending_quits = max(0, self.pending_quits - 1)
pending_quits = 0
def quit_main_loop(self):
"""Return control to the next level up."""
# We quit in an idle function, because successive calls to
# main_quit will do nothing if the main loop hasn't had time to
# quit. So we stagger calls to make sure that if this function
# is called multiple times (nested loops), it works as expected.
def idle_quit():
if self.pending_quits > 1:
quit_quit()
if Gtk.main_level() > 0:
Gtk.main_quit()
else:
self.pending_quits = max(0, self.pending_quits - 1)
return False
def quit_quit():
# Wait until we're actually out of this main loop
GLib.idle_add(idle_quit)
return False
if self.pending_quits == 0:
quit_quit()
self.pending_quits += 1
# vim:ai:et:sts=4:tw=80:sw=4:
You can’t perform that action at this time.