summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsegfault <segfault@riseup.net>2019-09-26 00:02:50 +0200
committersegfault <segfault@riseup.net>2019-10-01 23:35:17 +0200
commit85e9ffd6c7085637bfb5fa2f2f2e3164377ad567 (patch)
tree6b2562d0132c715323c354cde7cfcba81a8ac857
parentb94b3550c1653efdfa84696b209b9d92abdf8c7e (diff)
Major refactoring of the Greeter (refs: #17098)
-rw-r--r--.gitignore1
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/gui.py1144
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/language.py806
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/__init__.py0
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py128
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py230
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py153
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py114
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py103
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py (renamed from config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/persistence.py)6
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py (renamed from config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/physicalsecurity.py)0
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py (renamed from config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/rootaccess.py)0
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py135
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/__init__.py3
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py142
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py275
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/help_window.py72
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py401
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py128
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/popover.py40
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py179
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/setting.py56
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py36
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/utils.py41
-rw-r--r--config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py8
-rw-r--r--config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui.in497
-rw-r--r--config/chroot_local-includes/usr/share/tails/greeter/greeter.ui1748
-rw-r--r--config/chroot_local-includes/usr/share/tails/greeter/main.ui.in681
-rw-r--r--config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui.in182
-rw-r--r--config/chroot_local-includes/usr/share/tails/greeter/setting.ui.in56
-rwxr-xr-xconfig/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py42
-rw-r--r--po/POTFILES.in13
-rw-r--r--po/POTFILES.skip13
-rwxr-xr-xrefresh-translations11
34 files changed, 3675 insertions, 3769 deletions
diff --git a/.gitignore b/.gitignore
index 4c144db..903fa32 100644
--- a/.gitignore
+++ b/.gitignore
@@ -57,6 +57,7 @@
/config/chroot_local-includes/usr/share/desktop-directories/Tails.directory
/config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.root-terminal.policy
/config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.additional-software.policy
+/config/chroot_local-includes/usr/share/tails/greeter/*.ui
/config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/*.ui
/tmp/
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/gui.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/gui.py
deleted file mode 100644
index 91e4b9e..0000000
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/gui.py
+++ /dev/null
@@ -1,1144 +0,0 @@
-# -*- coding: utf-8 -*-/
-#
-# Copyright 2015-2016 Tails developers <tails@boum.org>
-#
-# 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 3 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, see <http://www.gnu.org/licenses/>
-
-import locale
-import logging
-import threading
-import webbrowser
-
-import gi
-
-gi.require_version('GLib', '2.0')
-from gi.repository import GLib # NOQA: E402
-gi.require_version('Gdk', '3.0')
-from gi.repository import Gdk # NOQA: E402
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk # NOQA: E402
-gi.require_version('Pango', '1.0')
-from gi.repository import Pango # NOQA: E402
-gi.require_version('WebKit2', '4.0')
-from gi.repository import WebKit2 # NOQA: E402
-
-import tailsgreeter # NOQA: E402
-import tailsgreeter.config # NOQA: E402
-import tailsgreeter.utils # NOQA: E402
-from tailsgreeter.language import TranslatableWindow # NOQA: E402
-
-from tailsgreeter import TRANSLATION_DOMAIN
-
-UI_FILE = 'greeter.ui'
-CSS_FILE = 'greeter.css'
-ICON_DIR = 'icons/'
-MAIN_WINDOW_PREFERRED_WIDTH = 620
-MAIN_WINDOW_PREFERRED_HEIGHT = 470
-ADD_SETTING_DIALOG_PREFERRED_WIDTH = 400
-HELP_WINDOW_PREFERRED_WIDTH = 800
-
-locale.bindtextdomain(TRANSLATION_DOMAIN, tailsgreeter.config.locales_path)
-# Mark translatable strings, but don't actually translate them, as we
-# delegate this to TranslatableWindow that handles on-the-fly language changes
-_ = lambda text: text # NOQA: E731
-
-
-class GreeterSetting(object):
- def __init__(self, setting_id):
- self.setting_id = setting_id
- self.listbox_container = None
- self.accel_key = None
- self._popover = None
-
- # The purpose of this method is that subclasses can override it to
- # do stuff when the popover is shown
- def show_popover(self):
- self._popover.set_visible(True)
-
- def hide_popover(self):
- self._popover.set_visible(False)
-
- def hide_popover_if_any(self):
- if self._popover:
- self.hide_popover()
-
- def toggle_popover(self):
- if self._popover.get_visible():
- self.hide_popover()
- else:
- self.show_popover()
-
- def has_popover(self):
- return self._popover is not None
-
- def cb_popover_closed(self, popover, user_data=None):
- self.listbox_container.unselect_all()
- return False
-
- @staticmethod
- def _add_popover(widget, content, closed_cb=None):
- popover = Gtk.Popover.new(widget)
- popover.set_position(Gtk.PositionType.BOTTOM)
- popover.add(content)
- if closed_cb:
- popover.connect('closed', closed_cb)
- return popover
-
- @staticmethod
- def _fill_tree_view(name, treeview):
- assert isinstance(name, str)
- assert isinstance(treeview, Gtk.TreeView)
- renderer = Gtk.CellRendererText()
- renderer.props.ellipsize = Pango.EllipsizeMode.END
- column = Gtk.TreeViewColumn(name, renderer, text=1)
- treeview.append_column(column)
-
-
-class RegionSetting(GreeterSetting):
- def __init__(self, setting_id, greeter, builder, setting_target=None):
- super().__init__(setting_id)
- self.greeter = greeter
-
- if not setting_target:
- setting_target = setting_id
- self.target = setting_target
-
- self.target.connect("notify::value", self.cb_value_changed)
-
- self.treestore = self.target.get_tree()
- self.build_ui(builder)
-
- def build_ui(self, builder):
- self.listbox_container = builder.get_object("listbox_language")
-
- listboxrow = builder.get_object(
- "listboxrow_{}".format(self.setting_id))
- self.label_value = builder.get_object(
- "label_{}_value".format(self.setting_id))
- box = builder.get_object(
- "box_{}_popover".format(self.setting_id))
- self.treeview = builder.get_object(
- "treeview_{}".format(self.setting_id))
- searchentry = builder.get_object(
- "searchentry_{}".format(self.setting_id))
-
- self._popover = GreeterSetting._add_popover(
- listboxrow, box, closed_cb=self.cb_popover_closed)
-
- GreeterSetting._fill_tree_view("", self.treeview)
- self.treestore_filtered = self.treestore.filter_new()
- self.treestore_filtered.set_visible_func(
- self.cb_liststore_filtered_visible_func, data=searchentry)
- self.treeview.set_model(self.treestore_filtered)
-
- searchentry.connect("search-changed",
- self.cb_searchentry_search_changed)
- searchentry.connect("activate", self.cb_searchentry_activate)
- self.treeview.connect("row-activated", self.cb_treeview_row_activated)
-
- def cb_searchentry_activate(self, searchentry, user_data=None):
- """Selects the topmost item in the treeview when pressing Enter"""
- if searchentry.get_text():
- self.treeview.row_activated(Gtk.TreePath.new_from_string("0"),
- self.treeview.get_column(0))
- else:
- self.hide_popover()
-
- def cb_searchentry_search_changed(self, searchentry, user_data=None):
- self.treestore_filtered.refilter()
- if searchentry.get_text():
- self.treeview.expand_all()
- self.treeview.scroll_to_point(0, 0) # scroll to top
- else:
- self.treeview.collapse_all()
- return False
-
- def cb_treeview_row_activated(self, treeview, path, column,
- user_data=None):
- treemodel = treeview.get_model()
- code = treemodel.get_value(treemodel.get_iter(path), 0)
- name = treemodel.get_value(treemodel.get_iter(path), 1)
-
- self.label_value.set_label(name)
- self.hide_popover()
-
- self.target.set_value(code)
-
- def cb_value_changed(self, obj, param):
- logging.debug("refreshing {}".format(self.target))
- self.label_value.set_label(self.target.get_name())
-
- def treeview_select_line(model, path, iter, data):
- if model.get_value(iter, 0) == data:
- self.treeview.get_selection().select_iter(iter)
- self.treeview.scroll_to_cell(path, use_align=True,
- row_align=0.5)
- return True
- else:
- return False
-
- self.treestore_filtered.foreach(
- treeview_select_line,
- self.target.get_value())
-
- def cb_liststore_filtered_visible_func(self, model, treeiter, searchentry):
- search_query = searchentry.get_text().lower()
- if not search_query:
- return True
-
- # Does the current node match the search?
- value = model.get_value(treeiter, 1).lower()
- if search_query in value:
- return True
-
- # Does the parent node match the search?
- treepath = model.get_path(treeiter)
- parent_treepath = treepath.copy()
- parent_treepath.up()
- if parent_treepath.get_depth() == 1:
- # treepath is now the parent
- parent_value = model.get_value(model.get_iter(parent_treepath), 0)
- return search_query in parent_value
-
- # Does any of the children nodes match the search?
- children_treeiter = model.iter_children(treeiter)
- while children_treeiter:
- child_value = model.get_value(children_treeiter, 0)
- if search_query in child_value:
- return True
- children_treeiter = model.iter_next(children_treeiter)
-
- return False
-
-
-class TextSetting(RegionSetting):
- def __init__(self, greeter, builder):
- super().__init__("text", greeter, builder,
- greeter.localisationsettings.text)
-
-
-class KeyboardSetting(RegionSetting):
- def __init__(self, greeter, builder):
- super().__init__("keyboard", greeter, builder,
- greeter.localisationsettings.layout)
-
-
-class FormatsSetting(RegionSetting):
- def __init__(self, greeter, builder):
- super().__init__("formats", greeter, builder,
- greeter.localisationsettings.formats)
-
-
-class AdditionalSetting(GreeterSetting):
- def __init__(self, setting_id, greeter, builder):
- super().__init__(setting_id)
- self.greeter = greeter
- self.build_ui(builder)
-
- def build_ui(self, builder):
- self.listbox_container = builder.get_object("listbox_settings")
-
- self.listboxrow = builder.get_object(
- "listboxrow_{}".format(self.setting_id))
- self.label_value = builder.get_object(
- "label_{}_value".format(self.setting_id))
- self.box = builder.get_object(
- "box_{}_popover".format(self.setting_id))
-
- def build_popover(self):
- self._popover = GreeterSetting._add_popover(
- self.listboxrow, self.box, closed_cb=self.cb_popover_closed)
-
- def apply(self):
- pass
-
-
-class AdminSetting(AdditionalSetting):
- def __init__(self, greeter, builder):
- super().__init__("admin", greeter, builder)
- self.accel_key = Gdk.KEY_a
-
- def build_ui(self, builder):
- super().build_ui(builder)
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'box_admin_password',
- 'box_admin_verify',
- 'button_admin_disable',
- 'entry_admin_password',
- 'entry_admin_verify',
- 'label_admin_value',
- ])
-
- def show_popover(self):
- super().show_popover()
- self.entry_admin_verify.set_icon_from_icon_name(
- Gtk.EntryIconPosition.SECONDARY, None)
-
- # XXX-non-blocker: avoid mixing business logic with GUI code?
- # The "check and return a boolean" operation should live in a pure function
- # outside of this file, and a method here should use it to update the GUI
- # accordingly.
- def check(self):
- password = self.entry_admin_password.get_text()
- # Don't accept an empty password
- if not password:
- return False
-
- verify = self.entry_admin_verify.get_text()
- if verify and verify == password:
- icon = 'emblem-ok-symbolic'
- elif verify and not (verify == password):
- icon = 'dialog-warning-symbolic'
- else:
- icon = None
- self.entry_admin_verify.set_icon_from_icon_name(
- Gtk.EntryIconPosition.SECONDARY, icon)
- return (verify == password)
-
- def apply(self):
- if self.check():
- password = self.entry_admin_password.get_text()
- self.greeter.rootaccess.password = password
- # XXX-non-blocker: the action at a distance on next line is
- # scary; better return the admin password (or False if the check
- # fails), and let the caller modify its own state... or do some
- # slightly less scary action at a distance?
- # Same comment wrt. the disable method, and more
- # generally it feels wrong that each *Setting objects gets a
- # greeter attribute they can mess with as they want.
- self.label_admin_value.set_label(
- tailsgreeter.utils.get_on_off_string(password, default=None))
- self.box_admin_password.set_visible(False)
- self.box_admin_verify.set_visible(False)
- self.button_admin_disable.set_visible(True)
- return True
- else:
- return False
-
- def disable(self):
- self.greeter.rootaccess.password = None
- self.label_admin_value.set_label(
- tailsgreeter.utils.get_on_off_string(None, default=None))
- self.entry_admin_password.set_text("")
- self.entry_admin_verify.set_text("")
- self.box_admin_password.set_visible(True)
- self.box_admin_verify.set_visible(True)
- self.button_admin_disable.set_visible(False)
-
-
-class MACSpoofSetting(AdditionalSetting):
- def __init__(self, greeter, builder):
- super().__init__("macspoof", greeter, builder)
- self.accel_key = Gdk.KEY_m
- self.spoofing_enabled = True
-
- def build_ui(self, builder):
- super().build_ui(builder)
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'image_macspoof_off',
- 'image_macspoof_on',
- 'label_macspoof_value',
- 'listboxrow_macspoof_off',
- 'listboxrow_macspoof_on',
- ])
-
- def row_activated(self, row):
- if row == self.listboxrow_macspoof_on:
- self.spoofing_enabled = True
- self.image_macspoof_on.set_visible(True)
- self.image_macspoof_off.set_visible(False)
- elif row == self.listboxrow_macspoof_off:
- self.spoofing_enabled = False
- self.image_macspoof_off.set_visible(True)
- self.image_macspoof_on.set_visible(False)
- self.label_macspoof_value.set_label(
- tailsgreeter.utils.get_on_off_string(self.spoofing_enabled, default=True))
-
- def apply(self):
- self.greeter.physical_security.macspoof = self.spoofing_enabled
-
-
-class NetworkSetting(AdditionalSetting):
- def __init__(self, greeter, builder):
- super().__init__("network", greeter, builder)
- self.accel_key = Gdk.KEY_n
- self.netconf = self.greeter.physical_security.NETCONF_DIRECT
-
- def build_ui(self, builder):
- super().build_ui(builder)
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'infobar_network',
- 'image_network_clear',
- 'image_network_specific',
- 'image_network_off',
- 'label_network_value',
- 'listboxrow_network_clear',
- 'listboxrow_network_specific',
- 'listboxrow_network_off',
- ])
-
- def row_activated(self, row):
- if row == self.listboxrow_network_clear:
- self.netconf = self.greeter.physical_security.NETCONF_DIRECT
- self.image_network_clear.set_visible(True)
- self.image_network_specific.set_visible(False)
- self.image_network_off.set_visible(False)
- self.label_network_value.set_label(_("Direct (default)"))
- elif row == self.listboxrow_network_specific:
- self.netconf = self.greeter.physical_security.NETCONF_OBSTACLE
- self.image_network_specific.set_visible(True)
- self.image_network_clear.set_visible(False)
- self.image_network_off.set_visible(False)
- self.label_network_value.set_label(_("Bridge & Proxy"))
- elif row == self.listboxrow_network_off:
- self.netconf = self.greeter.physical_security.NETCONF_DISABLED
- self.image_network_off.set_visible(True)
- self.image_network_specific.set_visible(False)
- self.image_network_clear.set_visible(False)
- self.label_network_value.set_label(_("Offline"))
-
- def apply(self):
- self.greeter.physical_security.netconf = self.netconf
- if (self.greeter.physical_security.netconf ==
- self.greeter.physical_security.NETCONF_OBSTACLE):
- self.infobar_network.set_visible(True)
- else:
- self.infobar_network.set_visible(False)
-
-
-class PersistentStorage(object):
- def __init__(self, greeter, builder):
- self.greeter = greeter
- self.build_ui(builder)
-
- def build_ui(self, builder):
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'box_storage',
- 'box_storage_unlock',
- 'box_storage_unlocked',
- 'button_storage_configure',
- 'button_storage_unlock',
- 'checkbutton_storage_show_passphrase',
- 'entry_storage_passphrase',
- 'image_storage_state',
- 'infobar_persistence',
- 'label_infobar_persistence',
- 'spinner_storage_unlock',
- ])
-
- if self.greeter.persistence.has_persistence():
- self.button_storage_configure.set_visible(False)
- self.checkbutton_storage_show_passphrase.set_visible(True)
- self.box_storage_unlock.set_visible(True)
- self.image_storage_state.set_visible(True)
- self.entry_storage_passphrase.set_visible(True)
- self.spinner_storage_unlock.set_visible(False)
- else:
- # XXX-future: We have a nice button to configure the persistence
- # but nothing is implemented to do so currently. So let's
- # hide the whole thing for now.
- self.box_storage.set_visible(False)
-
- def configure(self):
- # XXX-future: this should launch the configuration of the persistence.
- logging.warn("User would be able to set up an encrypted storage.")
- raise NotImplementedError
-
- def lock(self):
- if self.greeter.persistence.lock():
- self.button_storage_lock.set_visible(False)
- self.checkbutton_storage_show_passphrase.set_visible(True)
- self.box_storage_unlock.set_visible(True)
- self.image_storage_state.set_visible(True)
- self.image_storage_state.set_from_icon_name(
- 'tails-locked', Gtk.IconSize.BUTTON)
- self.entry_storage_passphrase.set_visible(True)
- self.entry_storage_passphrase.set_sensitive(True)
- self.button_storage_unlock.set_visible(True)
- self.button_storage_unlock.set_sensitive(True)
- self.button_storage_unlock.set_label(_("Unlock"))
- else:
- self.label_infobar_persistence.set_label(
- _("Failed to relock persistent storage."))
- self.infobar_persistence.set_visible(True)
-
- def passphrase_changed(self, editable):
- # Remove warning icon
- editable.set_icon_from_icon_name(
- Gtk.EntryIconPosition.SECONDARY,
- None)
-
- def unlock(self, unlocked_cb=None, failed_cb=None):
- if not unlocked_cb:
- unlocked_cb = self.cb_unlocked
- if not failed_cb:
- failed_cb = self.cb_unlock_failed
-
- self.checkbutton_storage_show_passphrase.set_visible(False)
- self.entry_storage_passphrase.set_sensitive(False)
- self.button_storage_unlock.set_sensitive(False)
- self.button_storage_unlock.set_label(_("Unlocking…"))
- self.image_storage_state.set_visible(False)
- self.spinner_storage_unlock.set_visible(True)
-
- passphrase = self.entry_storage_passphrase.get_text()
-
- # Let's execute the unlocking in a thread
- def do_unlock_storage(unlock_method, passphrase, unlocked_cb,
- failed_cb):
- if unlock_method(passphrase):
- GLib.idle_add(unlocked_cb)
- else:
- GLib.idle_add(failed_cb)
-
- unlocking_thread = threading.Thread(
- target=do_unlock_storage,
- args=(self.greeter.persistence.unlock,
- passphrase,
- unlocked_cb,
- failed_cb)
-
- )
- unlocking_thread.start()
-
- def cb_unlock_failed(self):
- logging.debug("Storage unlock failed")
- self.checkbutton_storage_show_passphrase.set_visible(True)
- self.entry_storage_passphrase.set_sensitive(True)
- self.button_storage_unlock.set_sensitive(True)
- self.button_storage_unlock.set_label(_("Unlock"))
- self.image_storage_state.set_visible(True)
- self.spinner_storage_unlock.set_visible(False)
- self.label_infobar_persistence.set_label(
- _("Cannot unlock encrypted storage with this passphrase."))
- self.infobar_persistence.set_visible(True)
- self.entry_storage_passphrase.select_region(0, -1)
- self.entry_storage_passphrase.set_icon_from_icon_name(
- Gtk.EntryIconPosition.SECONDARY,
- 'dialog-warning-symbolic')
- self.entry_storage_passphrase.grab_focus()
-
- def cb_unlocked(self):
- logging.debug("Storage unlocked")
- self.spinner_storage_unlock.set_visible(False)
- self.entry_storage_passphrase.set_visible(False)
- self.button_storage_unlock.set_visible(False)
- self.infobar_persistence.set_visible(False)
- self.image_storage_state.set_from_icon_name('tails-unlocked',
- Gtk.IconSize.BUTTON)
- self.image_storage_state.set_visible(True)
- self.box_storage_unlocked.set_visible(True)
-
-
-class GreeterSettingsCollection(object):
- def __init__(self, greeter, builder):
- # Region settings
- self.text = TextSetting(greeter, builder)
- self.keyboard = KeyboardSetting(greeter, builder)
- self.formats = FormatsSetting(greeter, builder)
-
- # Additional settings views
- self.admin = AdminSetting(greeter, builder)
- self.macspoof = MACSpoofSetting(greeter, builder)
- self.network = NetworkSetting(greeter, builder)
-
- def __getitem__(self, key):
- return self.__getattribute__(key)
-
- def __iter__(self):
- return iter([getattr(self, k) for k in self.__dict__.keys()])
-
-
-class DialogAddSetting(Gtk.Dialog):
- def __init__(self, builder, settings):
- super().__init__(use_header_bar=True)
- self.build_ui(builder)
- self.settings = settings
-
- def build_ui(self, builder):
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'box_admin_popover',
- 'box_macspoof_popover',
- 'box_network_popover',
- 'entry_admin_password',
- 'listbox_add_setting',
- 'listboxrow_admin',
- 'listboxrow_macspoof',
- 'listboxrow_network',
- ])
-
- self.set_transient_for(self)
- self.set_title(_("Additional Settings"))
- self.set_default_size(-1, ADD_SETTING_DIALOG_PREFERRED_WIDTH)
-
- sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
-
- accelgroup = Gtk.AccelGroup.new()
- self.add_accel_group(accelgroup)
-
- self.button_cancel = self.add_button(_("Cancel"),
- Gtk.ResponseType.CANCEL)
- accelgroup.connect(Gdk.KEY_Escape, 0, 0,
- self.cb_accelgroup_cancel_activated)
- sizegroup.add_widget(self.button_cancel)
-
- self.button_add = self.add_button(_("Add"), Gtk.ResponseType.YES)
- Gtk.StyleContext.add_class(self.button_add.get_style_context(),
- 'suggested-action')
- sizegroup.add_widget(self.button_add)
- accelgroup.connect(Gdk.KEY_Return, 0, 0,
- self.cb_accelgroup_add_activated)
- self.button_add.set_visible(False)
-
- self.button_back = Gtk.Button.new_with_label(_("Back"))
- self.button_back.set_visible(False)
- self.button_back.connect('clicked', self.cb_button_back_clicked, None)
- sizegroup.add_widget(self.button_back)
- accelgroup.connect(Gdk.KEY_Back, 0, 0,
- self.cb_accelgroup_back_activated)
- # These key bindings are copied from Firefox, and are the same with
- # right-to-left languages.
- accelgroup.connect(Gdk.KEY_Left, Gdk.ModifierType.MOD1_MASK, 0,
- self.cb_accelgroup_back_activated)
- accelgroup.connect(Gdk.KEY_KP_Left, Gdk.ModifierType.MOD1_MASK, 0,
- self.cb_accelgroup_back_activated)
- self.get_header_bar().pack_end(self.button_back)
-
- self.stack = Gtk.Stack()
- self.stack.add_named(self.listbox_add_setting, "setting-type")
- self.listbox_add_setting.set_valign(Gtk.Align.FILL)
- self.listbox_add_setting.set_vexpand(True)
- self.stack.set_visible(True)
- # XXX: is SLIDE_LEFT_RIGHT automatically inversed in RTL mode?
- self.stack.set_transition_type(
- Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
- self.get_content_area().add(self.stack)
-
- def go_back(self):
- self.stack.set_visible_child_name('setting-type')
- self.button_back.set_visible(False)
- self.button_add.set_visible(False)
- self.stack.remove(self.stack.get_child_by_name('setting-details'))
-
- def listbox_focus(self):
- # Workaround autoselection of 1st item on focus
- self.listbox_add_setting.unselect_all()
-
- def listbox_row_activated(self, row):
- if not row: # this happens when the row gets unselected
- return False
- setting_id = tailsgreeter.utils.setting_id_from_row(row)
- self.stack.add_named(self.settings[setting_id].box, 'setting-details')
- self.stack.set_visible_child_name('setting-details')
- # XXX: this is an ugly workaround for setting the default window state
- if setting_id == "admin":
- self.button_add.set_sensitive(self.settings.admin.check())
- self.entry_admin_password.grab_focus()
- else:
- self.button_add.set_sensitive(True)
- self.button_back.set_visible(True)
- self.button_add.set_visible(True)
-
- def run(self, setting_id=None):
- if setting_id:
- row = self.settings[setting_id].listboxrow
- row.emit("activate")
- else:
- self.stack.set_visible_child_name('setting-type')
- self.button_back.set_visible(False)
- self.button_add.set_visible(False)
- return super().run()
-
- def cb_accelgroup_add_activated(self, accel_group, accelerable, keyval,
- modifier, user_data=None):
- if self.button_add.get_visible() and self.button_add.get_sensitive():
- self.response(Gtk.ResponseType.YES)
- return False
-
- def cb_accelgroup_back_activated(self, accel_group, accelerable, keyval,
- modifier, user_data=None):
- self.go_back()
- return False
-
- def cb_accelgroup_cancel_activated(self, accel_group, accelerable, keyval,
- modifier, user_data=None):
- self.response(Gtk.ResponseType.CANCEL)
- return True # disable the default callbacks that destroys the dialog
-
- def cb_button_back_clicked(self, widget, user_data=None):
- self.go_back()
- return False
-
-
-class GreeterMainWindow(Gtk.Window, TranslatableWindow):
-
- def __init__(self, greeter):
- Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE))
- TranslatableWindow.__init__(self, self)
- self.greeter = greeter
-
- self._build_ui()
- self.store_translations(self)
-
- self.connect('delete-event', self.cb_window_delete_event, None)
- self.set_position(Gtk.WindowPosition.CENTER)
-
- # Utility methods
-
- def __align_checkbuttons(self):
- """Put the text before the checkbox rather than the opposite (assuming
- LTR), because it's what designers want."""
- for checkbutton in [self.checkbutton_language_save,
- self.checkbutton_storage_show_passphrase,
- self.checkbutton_settings_save]:
- checkbutton.set_direction(not Gtk.Widget.get_default_direction())
-
- def _build_accelerators(self):
- accelgroup = Gtk.AccelGroup.new()
- self.add_accel_group(accelgroup)
- for accel_key in [s.accel_key for s in self.settings if s.accel_key]:
- accelgroup.connect(
- accel_key,
- Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK,
- Gtk.AccelFlags.VISIBLE,
- self.cb_accelgroup_setting_activated)
-
- def _build_headerbar(self):
- headerbar = Gtk.HeaderBar()
- headerbar_sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
-
- self.button_shutdown = Gtk.Button.new_with_label(_("Shutdown"))
- self.button_shutdown.connect('clicked', self.cb_button_shutdown_clicked)
- headerbar_sizegroup.add_widget(self.button_shutdown)
- headerbar.pack_start(self.button_shutdown)
-
- self.button_start = Gtk.Button.new_with_mnemonic(_("_Start Tails"))
- Gtk.StyleContext.add_class(self.button_start.get_style_context(),
- 'suggested-action')
- self.button_start.connect('clicked', self.cb_button_start_clicked)
- headerbar_sizegroup.add_widget(self.button_start)
- headerbar.pack_end(self.button_start)
-
- headerbar.show_all()
-
- return headerbar
-
- def _build_ui(self):
- # Load custom CSS
- css_provider = Gtk.CssProvider.new()
- css_provider.load_from_path(tailsgreeter.config.data_path + CSS_FILE)
- Gtk.StyleContext.add_provider_for_screen(
- Gdk.Screen.get_default(),
- css_provider,
- Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
-
- # Load UI interface definition
- builder = Gtk.Builder()
- builder.set_translation_domain(TRANSLATION_DOMAIN)
- builder.add_from_file(tailsgreeter.config.data_path + UI_FILE)
- builder.connect_signals(self)
-
- for widget in builder.get_objects():
- # Store translations for the builder objects
- self.store_translations(widget)
- # Workaround Gtk bug #710888 - GtkInfoBar not shown after calling
- # gtk_widget_show:
- # https://bugzilla.gnome.org/show_bug.cgi?id=710888
- if isinstance(widget, Gtk.InfoBar):
- revealer = widget.get_template_child(Gtk.InfoBar, 'revealer')
- revealer.set_transition_type(Gtk.RevealerTransitionType.NONE)
-
- tailsgreeter.utils.import_builder_objects(self, builder, [
- 'box_admin_popover',
- 'box_language',
- 'box_language_header',
- 'box_macspoof_popover',
- 'box_main',
- 'box_network_popover',
- 'box_settings',
- 'box_settings_header',
- 'box_settings_values',
- 'box_storage',
- 'box_storage_unlock',
- 'box_storage_unlocked',
- 'button_storage_configure',
- 'checkbutton_language_save',
- 'checkbutton_settings_save',
- 'checkbutton_storage_show_passphrase',
- 'entry_admin_verify',
- 'entry_storage_passphrase',
- 'frame_language',
- 'label_settings_default',
- 'listbox_add_setting',
- 'listbox_settings',
- 'listboxrow_formats',
- 'listboxrow_keyboard',
- 'listboxrow_admin',
- 'listboxrow_macspoof',
- 'listboxrow_network',
- 'listboxrow_text',
- 'toolbutton_settings_add',
- ])
-
- # Set preferred width
- self.set_default_size(min(Gdk.Screen.get_default().get_width(),
- MAIN_WINDOW_PREFERRED_WIDTH),
- min(Gdk.Screen.get_default().get_height(),
- MAIN_WINDOW_PREFERRED_HEIGHT))
-
- # Add our icon dir to icon theme
- icon_theme = Gtk.IconTheme.get_default()
- icon_theme.prepend_search_path(
- tailsgreeter.config.data_path + ICON_DIR)
-
- # Add placeholder to settings ListBox
- self.listbox_settings.set_placeholder(self.label_settings_default)
-
- # Settings view
- self.settings = GreeterSettingsCollection(self.greeter, builder)
-
- # Persistent storage
- self.persistent_storage = PersistentStorage(self.greeter, builder)
-
- # Add children to ApplicationWindow
- self.add(self.box_main)
- self.set_titlebar(self._build_headerbar())
-
- # Set keyboard focus chain
- self.__set_focus_chain()
-
- # Setup keyboard accelerators
- self._build_accelerators()
-
- # Adapt CheckButtons direction to the mockups
- self.__align_checkbuttons()
-
- # Add settings dialog
- self.dialog_add_setting = DialogAddSetting(builder, self.settings)
- self.dialog_add_setting.set_transient_for(self)
- self.store_translations(self.dialog_add_setting)
-
- def __set_focus_chain(self):
- self.box_language.set_focus_chain([
- self.frame_language,
- self.box_language_header])
- self.box_storage.set_focus_chain([
- self.box_storage_unlock,
- self.box_storage_unlocked,
- self.button_storage_configure,
- self.checkbutton_storage_show_passphrase])
- self.box_settings.set_focus_chain([
- self.box_settings_values,
- self.box_settings_header])
-
- # TranslatableWindow implementation
- def translate_to(self, lang):
- TranslatableWindow.translate_to(self, lang)
- self.__align_checkbuttons()
-
- # Actions
-
- def check_and_login(self):
- if (self.greeter.persistence.has_persistence() and
- self.entry_storage_passphrase.get_text() and
- not self.greeter.persistence.is_unlocked):
- logging.debug("Unlocking persistent storage before login")
- self.persistent_storage.unlock(unlocked_cb=self.finish_login)
- else:
- self.finish_login()
-
- def finish_login(self):
- logging.info("Starting the session")
- self.greeter.login()
- return False
-
- def setting_add(self, setting_id=None):
- response = self.dialog_add_setting.run(setting_id)
- if response == Gtk.ResponseType.YES:
- row = self.listbox_add_setting.get_selected_row()
- setting_id = tailsgreeter.utils.setting_id_from_row(row)
- box = self.__getattribute__("box_{}_popover".format(setting_id))
-
- # The setting used to be applied by the dialog itself, but there
- # we don't know the response type in all cases. For example, we
- # previously didn't apply the admin password in all cases if the
- # "Add" button was clicked to close the dialog (#13447).
- self.settings[setting_id].apply()
-
- self.listbox_add_setting.remove(row)
- self.listbox_settings.add(row)
- self.dialog_add_setting.set_visible(False)
- self.dialog_add_setting.stack.remove(box)
- self.settings[setting_id].build_popover()
-
- self.listbox_settings.unselect_all()
- if True not in [c.get_visible() for c in
- self.listbox_add_setting.get_children()]:
- self.toolbutton_settings_add.set_sensitive(False)
- self.dialog_add_setting.set_visible(False)
- else:
- old_details = self.dialog_add_setting.stack.get_child_by_name(
- 'setting-details')
- if old_details:
- self.dialog_add_setting.stack.remove(old_details)
- self.dialog_add_setting.set_visible(False)
-
- def setting_edit(self, setting_id):
- if self.settings[setting_id].has_popover():
- self.settings[setting_id].listboxrow.emit("activate")
- else:
- self.setting_add(setting_id)
-
- def setting_admin_disable(self):
- self.settings.admin.disable()
- self.settings.admin.hide_popover_if_any()
-
- def setting_network_close(self, only_if_popover=False):
- if self.settings.network.hide_popover_if_any():
- # we are in the popover
- self.settings.network.show_bridge_info_if_needed()
- elif not only_if_popover:
- # We are in the "Add Additional Setting" dialog
- self.dialog_add_setting.response(Gtk.ResponseType.YES)
-
- def show(self):
- super().show()
- self.button_start.grab_focus()
- self.get_root_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.ARROW))
-
- # Callbacks
-
- # XXX-refactor: some of these callbacks could be totally moved out of this
- # class
-
- def cb_accelgroup_setting_activated(self, accel_group, accelerable,
- keyval, modifier):
- for setting in self.settings:
- if setting.accel_key == keyval:
- self.setting_edit(setting.setting_id)
- return False
-
- def cb_button_admin_disable_clicked(self, widget, user_data=None):
- self.setting_admin_disable()
-
- def cb_linkbutton_help_activate(self, linkbutton, user_data=None):
- linkbutton.set_sensitive(False)
- # Display progress cursor and update the UI
- self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
- while Gtk.events_pending():
- Gtk.main_iteration()
- # Note that we add the "file://" part here, not in the URI.
- # We're forced to add this
- # callback *in addition* to the standard one (Gtk.show_uri),
- # which will do nothing for uri:s without a protocol
- # part. This is critical since we otherwise would open the
- # default browser (iceweasel) in T-G. If pygtk had a mechanism
- # like gtk's g_signal_handler_find() this could be dealt with
- # in a less messy way by just removing the default handler.
- page = linkbutton.get_uri()
- uri = "file:///usr/share/doc/tails/website/" + page
- logging.debug("Opening help window for {}".format(uri))
- helpwindow = GreeterHelpWindow(uri)
- helpwindow.show()
-
- def restore_linkbutton_status(widget, event, linkbutton):
- linkbutton.set_sensitive(True)
- return False
-
- helpwindow.connect('delete-event', restore_linkbutton_status,
- linkbutton)
- # Restore default cursor
- self.get_window().set_cursor(None)
-
- def cb_button_shutdown_clicked(self, widget, user_data=None):
- self.greeter.shutdown()
- return False
-
- def cb_button_start_clicked(self, widget, user_data=None):
- self.check_and_login()
- return False
-
- def cb_button_storage_configure_clicked(self, user_data=None):
- self.persistent_storage.configure()
- return False
-
- def cb_button_storage_lock_clicked(self, widget, user_data=None):
- self.persistent_storage.lock()
- return False
-
- def cb_button_storage_unlock_clicked(self, widget, user_data=None):
- self.persistent_storage.unlock()
- return False
-
- def cb_checkbutton_storage_show_passphrase_toggled(self, widget,
- user_data=None):
- self.entry_storage_passphrase.set_visibility(widget.get_active())
-
- def cb_entry_admin_changed(self, editable, user_data=None):
- passwords_match = self.settings.admin.check()
- self.dialog_add_setting.button_add.set_sensitive(passwords_match)
- return False
-
- def cb_entry_admin_activate(self, widget, user_data=None):
- passwords_match = self.settings.admin.check()
- if not passwords_match:
- self.dialog_add_setting.button_add.set_sensitive(False)
- self.entry_admin_verify.grab_focus()
- return False
-
- # The passwords match, so lets close the popover/dialog
- if self.settings.admin.has_popover():
- # For the popup we have to apply the password immediately
- self.settings.admin.apply()
- # Hide the popup
- self.settings.admin.hide_popover()
- else:
- # Close the dialog
- self.dialog_add_setting.response(Gtk.ResponseType.YES)
- return False
-
- def cb_entry_storage_passphrase_activated(self, entry, user_data=None):
- self.persistent_storage.unlock()
- return False
-
- def cb_entry_storage_passphrase_changed(self, editable, user_data=None):
- self.persistent_storage.passphrase_changed(editable)
- return False
-
- def cb_infobar_close(self, infobar, user_data=None):
- infobar.set_visible(False)
- return False
-
- def cb_infobar_response(self, infobar, response_id, user_data=None):
- infobar.set_visible(False)
- return False
-
- def cb_listbox_add_setting_focus(self, widget, direction, user_data=None):
- self.dialog_add_setting.listbox_focus()
- return False
-
- def cb_listbox_add_setting_row_activated(self, listbox, row,
- user_data=None):
- self.dialog_add_setting.listbox_row_activated(row)
- return False
-
- def cb_listbox_language_row_activated(self, listbox, row, user_data=None):
- setting_id = tailsgreeter.utils.setting_id_from_row(row)
- self.settings[setting_id].toggle_popover()
- return False
-
- def cb_listbox_network_button_press(self, widget, event, user_data=None):
- if event.type == Gdk.EventType._2BUTTON_PRESS:
- self.setting_network_close()
- return False
-
- def cb_listbox_macspoof_row_activated(self, listbox, row, user_data=None):
- self.settings.macspoof.row_activated(row)
- if self.settings.macspoof.has_popover():
- self.settings.macspoof.apply()
- self.settings.macspoof.hide_popover()
-
- def cb_listbox_network_row_activated(self, listbox, row, user_data=None):
- self.settings.network.row_activated(row)
- if self.settings.network.has_popover():
- self.settings.network.apply()
- self.settings.network.hide_popover()
- return False
-
- def cb_listbox_settings_row_activated(self, listbox, row, user_data=None):
- setting_id = tailsgreeter.utils.setting_id_from_row(row)
- self.settings[setting_id].toggle_popover()
- return False
-
- def cb_toolbutton_settings_add_clicked(self, user_data=None):
- self.setting_add()
- return False
-
- def cb_toolbutton_settings_mnemonic_activate(self, widget, group_cycling):
- self.setting_add()
- return False
-
- def cb_window_delete_event(self, widget, event, user_data=None):
- # Don't close the toplevel window on user request (e.g. pressing
- # Alt+F4)
- return True
-
-
-class GreeterHelpWindow(Gtk.Window, TranslatableWindow):
- """Displays a modal HTML help window"""
-
- def __init__(self, uri):
- Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE))
- TranslatableWindow.__init__(self, self)
-
- self._build_ui()
- self.store_translations(self)
-
- self.load_uri(uri)
- # Replace the busy cursor set by the tails-greeter startup script with
- # the default cursor.
- self.get_window().set_cursor(None)
-
- def _build_ui(self):
- self.set_position(Gtk.WindowPosition.CENTER)
-
- # Create HeaderBar
- headerbar = Gtk.HeaderBar()
- headerbar.set_show_close_button(True)
- headerbar.show_all()
-
- # Create webview with custom stylesheet
- css = WebKit2.UserStyleSheet(
- ".sidebar, .banner { display: none; }",
- WebKit2.UserContentInjectedFrames.ALL_FRAMES,
- WebKit2.UserStyleLevel.USER,
- None,
- None)
- content_manager = WebKit2.UserContentManager()
- content_manager.add_style_sheet(css)
- self.webview = WebKit2.WebView.new_with_user_content_manager(
- content_manager)
- self.webview.connect("resource-load-started",
- self.cb_load_started)
- self.webview.show()
-
- scrolledwindow = Gtk.ScrolledWindow()
- scrolledwindow.add(self.webview)
- scrolledwindow.show()
-
- # Add children to ApplicationWindow
- self.add(scrolledwindow)
- self.set_titlebar(headerbar)
-
- def load_uri(self, uri):
- self.webview.load_uri(uri)
- self.resize(
- min(HELP_WINDOW_PREFERRED_WIDTH,
- self.get_screen().get_width()),
- self.get_screen().get_height())
- self.present()
-
- def cb_load_started(self, web_view, ressource, request):
- if not request.get_uri().startswith("file://"):
- webbrowser.open_new(request.get_uri())
- request.set_uri(web_view.get_uri())
-
-
-class GreeterBackgroundWindow(Gtk.ApplicationWindow):
-
- def __init__(self, app):
- Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE),
- application=app)
- self.override_background_color(
- Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1))
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/language.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/language.py
deleted file mode 100644
index 07bfdda..0000000
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/language.py
+++ /dev/null
@@ -1,806 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright 2012-2016 Tails developers <tails@boum.org>
-# Copyright 2011 Max <govnototalitarizm@gmail.com>
-# Copyright 2011 Martin Owens
-#
-# 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 3 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, see <http://www.gnu.org/licenses/>
-#
-"""Localization handling
-
-"""
-
-import logging
-import gettext
-import os
-import locale
-
-import gi
-import pycountry
-import pytz
-
-gi.require_version('Gio', '2.0')
-from gi.repository import Gio # NOQA: E402
-gi.require_version('GLib', '2.0')
-from gi.repository import GLib # NOQA: E402
-gi.require_version('GObject', '2.0')
-from gi.repository import GObject # NOQA: E402
-gi.require_version('GnomeDesktop', '3.0')
-from gi.repository import GnomeDesktop # NOQA: E402
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk # NOQA: E402
-gi.require_version('AccountsService', '1.0')
-from gi.repository import AccountsService # NOQA: E402
-
-import tailsgreeter.config # NOQA: E402
-
-from tailsgreeter import TRANSLATION_DOMAIN
-
-
-def ln_iso639_tri(ln_CC):
- """get iso639 3-letter code from a language code
-
- example: en -> eng"""
- return pycountry.languages.get(
- alpha2=language_from_locale(ln_CC)).terminology
-
-
-def ln_iso639_2_T_to_B(lng):
- """Convert a ISO-639-2/T code (e.g. deu for German) to a 639-2/B one
- (e.g. ger for German)"""
- return pycountry.languages.get(terminology=lng).bibliographic
-
-
-def language_from_locale(locale):
- """Obtain the language code from a locale code
-
- example: fr_FR -> fr"""
- return locale.split('_')[0]
-
-
-def languages_from_locales(locales):
- """Obtain a language code list from a locale code list
-
- example: [fr_FR, en_GB] -> [fr, en]"""
- language_codes = []
- for l in locales:
- language_code = language_from_locale(l)
- if language_code not in language_codes:
- language_codes.append(language_code)
- return language_codes
-
-
-def country_from_locale(locale):
- """Obtain the country code from a locale code
-
- example: fr_FR -> FR"""
- return locale.split('_')[1]
-
-
-def countries_from_locales(locales):
- """Obtain a country code list from a locale code list
-
- example: [fr_FR, en_GB] -> [FR, GB]"""
- country_codes = []
- for l in locales:
- country_code = country_from_locale(l)
- if country_code not in country_codes:
- country_codes.append(country_code)
- return country_codes
-
-
-class TranslatableWindow(object):
- """Interface providing functions to translate a window on the fly
- """
- retain_focus = True
- registered_windows = []
-
- def __init__(self, window):
- self.window = window
- self.translation = gettext.translation(
- TRANSLATION_DOMAIN,
- tailsgreeter.config.locales_path,
- fallback=True
- )
-
- self.containers = []
- self.labels = {}
- self.placeholder_texts = {}
- self.titles = {}
- self.tooltips = {}
-
- TranslatableWindow.registered_windows.append(window)
-
- @staticmethod
- def get_locale_direction(lang):
- """Return Gtk.TextDirection for lang
-
- This method in basically the same as Gtk.get_locale_direction
- (gtk_get_locale_direction in gtk/gtkmain.c), but it accepts a lang
- parameter rather than using current locale.
- """
- gtk_translation = gettext.translation("gtk30",
- languages=[str(lang)],
- fallback=True)
- logging.debug("%s has GTK translation: %s" % (lang, gtk_translation))
- # Translators: please do not translate this string (it is read from
- # Gtk translation)
- default_dir = gtk_translation.gettext("default:LTR")
- logging.debug("translation for direction is %s" % (default_dir))
- if default_dir == "default:RTL":
- logging.debug("%s is RTL" % lang)
- return Gtk.TextDirection.RTL
- else:
- return Gtk.TextDirection.LTR
-
- def store_translations(self, widget):
- """Store the elements that should be localised inside widget
-
- Go through all children of widget and store the translations
- of labels, tooltips and titles and the containers whose text direction
- should be updated when switching between LTR and RTL.
-
- This method should be called once the widgets are created"""
- if not isinstance(widget, Gtk.Widget):
- logging.debug("%s is not a Gtk.Widget" % widget)
- return None
- if isinstance(widget, Gtk.Label) or isinstance(widget, Gtk.Button):
- if widget not in self.labels:
- self.labels[widget] = widget.get_label()
- # Wrap set_label to get notified about string changes
- widget.original_set_label = widget.set_label
-
- def wrapped_set_label(text):
- self.labels[widget] = text
- widget.original_set_label(self.gettext(text))
- widget.set_label = wrapped_set_label
- elif isinstance(widget, Gtk.Entry):
- if widget not in self.placeholder_texts:
- self.placeholder_texts[widget] = widget.get_placeholder_text()
- elif isinstance(widget, Gtk.Container):
- self.containers.append(widget)
- if ((isinstance(widget, Gtk.HeaderBar) or
- isinstance(widget, Gtk.Window)) and
- widget not in self.titles and
- widget.get_title()):
- self.titles[widget] = widget.get_title()
- for child in widget.get_children():
- self.store_translations(child)
- else:
- logging.debug("W: unhandled widget: %s" % widget)
- if widget.get_has_tooltip():
- if widget not in self.tooltips:
- self.tooltips[widget] = widget.get_tooltip_text()
-
- def gettext(self, text):
- """Return text, translated if possible"""
- if self.translation and text:
- text = self.translation.gettext(text)
- return text
-
- def translate_to(self, lang):
- """Translate registered widgets on the fly
-
- Loop through widgets registered with store_translations and translate
- them on the fly"""
- logging.debug("translating %s to %s" % (self, lang))
- try:
- self.translation = gettext.translation(
- TRANSLATION_DOMAIN,
- tailsgreeter.config.locales_path, [str(lang)])
- except IOError:
- self.translation = None
-
- text_direction = self.get_locale_direction(lang)
- for container in self.containers:
- container.set_default_direction(text_direction)
- for widget, label in self.labels.items():
- if label:
- widget.original_set_label(self.gettext(label))
- for widget in self.placeholder_texts.keys():
- widget.set_placeholder_text(self.gettext(self.placeholder_texts[widget]))
- for widget in self.titles.keys():
- widget.set_title(self.gettext(self.titles[widget]))
- for widget in self.tooltips.keys():
- widget.set_tooltip_markup(self.gettext(self.tooltips[widget]))
- if (self.window.get_sensitive() and
- self.window.get_visible() and
- self.retain_focus):
- self.window.present()
-
- @staticmethod
- def translate_all(lang):
- for widget in TranslatableWindow.registered_windows:
- widget.translate_to(lang)
-
-
-class RegionSetting(GObject.Object):
-
- value = GObject.property(type=str)
- is_default = GObject.property(type=bool, default=True)
-
- def __init__(self, settings_object):
- super().__init__()
- self._settings = settings_object
-
- def get_value(self):
- return self.value
-
- # is_default will be used by subclasses to register default value
- def set_value(self, value, is_default=False):
- self.value = value
- if not is_default:
- self.is_default = False
- self._settings._apply_settings_to_upcoming_session()
-
- def get_name(self):
- raise NotImplementedError
-
- def get_tree(self):
- raise NotImplementedError
-
- def set_default(self):
- raise NotImplementedError
-
- def set_default_if_needed(self):
- """Update default value if it was not user choosen"""
- if self.is_default:
- self.set_default()
-
-
-class TextSetting(RegionSetting):
-
- def __init__(self, settings_object):
- super().__init__(settings_object)
- super().set_value('en_US', is_default=True)
-
- def get_tree(self, locale_codes=None):
- if not locale_codes:
- locale_codes = self._settings._system_locales_list
-
- treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
- GObject.TYPE_STRING) # name
-
- lang_codes = languages_from_locales(
- self._settings._system_locales_list)
- lang_codes.sort(key=lambda x: self._language_name(x).lower())
- for lang_code in lang_codes:
- language_name = self._language_name(lang_code)
- if not language_name:
- # Don't display languages without a name
- continue
- treeiter_language = treestore.append(parent=None)
- treestore.set(treeiter_language,
- 0, self.get_default_locale(lang_code))
- treestore.set(treeiter_language, 1, language_name)
- locale_codes = sorted(
- self.get_default_locales(lang_code),
- key=lambda x: self._locale_name(x).lower())
- if len(locale_codes) > 1:
- for locale_code in locale_codes:
- treeiter_locale = treestore.append(
- parent=treeiter_language)
- treestore.set(treeiter_locale, 0, locale_code)
- treestore.set(treeiter_locale, 1,
- self._locale_name(locale_code))
- return treestore
-
- def get_name(self):
- return self._locale_name(self.get_value())
-
- def get_default_locales(self, lang_code):
- """Return available locales for given language
-
- """
- if lang_code in self._settings._system_languages_dict:
- return self._settings._system_languages_dict[lang_code]
-
- def get_default_locale(self, lang_code=None):
- """Return default locale for given language
-
- Returns the 1st locale among:
- - the locale whose country name matches language name
- - the 1st locale for the language
- - en_US
- """
- default_locales = self.get_default_locales(lang_code)
- if default_locales:
- for locale_code in default_locales:
- if (country_from_locale(locale_code).lower() ==
- language_from_locale(locale_code)):
- return locale_code
- return default_locales[0]
- else:
- return 'en_US'
-
- def set_value(self, locale, is_default=False):
- super().set_value(locale, is_default)
- self.__apply_locale()
- self._settings.formats.set_default_if_needed() # XXX: notify
- self._settings.layout.set_default_if_needed() # XXX: notify
- if self._settings._locale_selected_cb:
- self._settings._locale_selected_cb(locale)
-
- def _language_name(self, lang_code):
- default_locale = 'C'
- local_locale = self.get_default_locale(lang_code)
- try:
- native_name = GnomeDesktop.get_language_from_code(
- lang_code, local_locale).capitalize()
- except AttributeError:
- return ""
- localized_name = GnomeDesktop.get_language_from_code(
- lang_code, default_locale).capitalize()
- if native_name == localized_name:
- return native_name
- else:
- return "{native} ({localized})".format(
- native=native_name, localized=localized_name)
-
- def _locale_name(self, locale_code):
- lang_code = language_from_locale(locale_code)
- country_code = country_from_locale(locale_code)
- language_name_locale = GnomeDesktop.get_language_from_code(lang_code)
- language_name_native = GnomeDesktop.get_language_from_code(
- lang_code, locale_code)
- country_name_locale = GnomeDesktop.get_country_from_code(country_code)
- country_name_native = GnomeDesktop.get_country_from_code(
- country_code, locale_code)
- try:
- if (language_name_native == language_name_locale and
- country_name_native == country_name_locale):
- return "{language} - {country}".format(
- language=language_name_native.capitalize(),
- country=country_name_native)
- else:
- return "{language} - {country} " \
- "({local_language} - {local_country})".format(
- language=language_name_native.capitalize(),
- country=country_name_native,
- local_language=language_name_locale.capitalize(),
- local_country=country_name_locale)
- except AttributeError:
- return locale_code
-
- def __apply_locale(self):
- locale_code = locale.normalize(
- self.get_value() + '.' + locale.getpreferredencoding())
- logging.debug("Setting session language to %s", locale_code)
- if self._settings._act_user:
- GLib.idle_add(
- lambda: self._settings._act_user.set_language(locale_code))
- else:
- logging.warning("AccountsManager not ready")
-
-
-class FormatSetting(RegionSetting):
- def __init__(self, settings_object):
- super().__init__(settings_object)
- super().set_value('en_US', is_default=True)
-
- def get_tree(self, format_codes=None):
- if not format_codes:
- format_codes = self._settings._system_locales_list
-
- treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
- GObject.TYPE_STRING) # name
-
- format_codes = countries_from_locales(
- self._settings._system_locales_list)
- format_codes.sort(key=lambda x: self._country_name(x).lower())
- logging.debug("format_codes=%s" % format_codes)
- for format_code in format_codes:
- format_name = self._country_name(format_code)
- if not format_name:
- # Don't display languages without a name
- continue
- treeiter_format = treestore.append(parent=None)
- treestore.set(treeiter_format,
- 0, self.get_default_locale(format_code))
- treestore.set(treeiter_format, 1, format_name)
- locale_codes = sorted(
- self.get_default_locales(format_code),
- key=lambda x: self._locale_name(x).lower())
- if len(locale_codes) > 1:
- for locale_code in locale_codes:
- treeiter_locale = treestore.append(
- parent=treeiter_format)
- treestore.set(treeiter_locale, 0, locale_code)
- treestore.set(treeiter_locale, 1,
- self._locale_name(locale_code))
- return treestore
-
- def get_name(self):
- return self._locale_name(self.get_value())
-
- def get_default_locales(self, country_code):
- """Return available locales for given country
-
- """
- if country_code in self._settings._system_formats_dict:
- return self._settings._system_formats_dict[country_code]
-
- def get_default_locale(self, country_code=None):
- """Return default locale for given country
-
- Returns the 1st locale among:
- - the locale whose country name matches country name
- - the 1st locale for the language
- - en_US
- """
- default_locales = self.get_default_locales(country_code)
- if default_locales:
- for locale_code in default_locales:
- if (country_from_locale(locale_code).lower() ==
- language_from_locale(locale_code)):
- return locale_code
- return default_locales[0]
- else:
- return 'en_US'
-
- def _country_name(self, country_code):
- default_locale = 'C'
- local_locale = self.get_default_locale(country_code)
- native_name = GnomeDesktop.get_country_from_code(
- country_code, local_locale)
- if not native_name:
- return ""
- localized_name = GnomeDesktop.get_country_from_code(
- country_code, default_locale)
- if native_name == localized_name:
- return native_name
- else:
- return "{native} ({localized})".format(
- native=native_name, localized=localized_name)
-
- def _locale_name(self, locale_code):
- lang_code = language_from_locale(locale_code)
- country_code = country_from_locale(locale_code)
- language_name_locale = GnomeDesktop.get_language_from_code(lang_code)
- language_name_native = GnomeDesktop.get_language_from_code(
- lang_code, locale_code)
- country_name_locale = GnomeDesktop.get_country_from_code(country_code)
- country_name_native = GnomeDesktop.get_country_from_code(
- country_code, locale_code)
- try:
- if (language_name_native == language_name_locale and
- country_name_native == country_name_locale):
- return "{country} - {language}".format(
- language=language_name_native.capitalize(),
- country=country_name_native)
- else:
- return "{country} - {language} " \
- "({local_country} - {local_language})".format(
- language=language_name_native.capitalize(),
- country=country_name_native,
- local_language=language_name_locale.capitalize(),
- local_country=country_name_locale)
- except AttributeError:
- return locale_code
-
- def set_default(self):
- """Set default format for current language
-
- Select the same locale for formats that the language
- """
- default_format = self._settings.text.get_value()
- logging.debug("setting default formats to %s" % default_format)
- self.set_value(default_format, is_default=True)
-
-
-class LayoutSetting(RegionSetting):
-
- def __init__(self, settings_object):
- super().__init__(settings_object)
- super().set_value('en+us', is_default=True)
- self.__xklinfo = GnomeDesktop.XkbInfo()
-
- def get_tree(self, layout_codes=None):
- if not layout_codes:
- layout_codes = self.get_all()
-
- treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
- GObject.TYPE_STRING) # name
- layouts = self._layouts_split_names(layout_codes)
- for group_name in sorted(layouts.keys()):
- layout_codes = sorted(
- layouts[group_name],
- key=lambda x: self._layout_name(x).lower())
- treeiter_group = treestore.append(parent=None)
- # we fill the title with the 1st layout of the group
- treestore.set(treeiter_group, 0, layout_codes[0])
- treestore.set(treeiter_group, 1, group_name)
- if len(layout_codes) > 1:
- for layout_code in layout_codes:
- treeiter_layout = treestore.append(parent=treeiter_group)
- treestore.set(treeiter_layout, 0, layout_code)
- treestore.set(treeiter_layout, 1,
- self._layout_name(layout_code))
- return treestore
-
- def get_name(self):
- return self._layout_name(self.get_value())
-
- def get_all(self):
- """Return a list of all keyboard layout codes
-
- """
- return self.__xklinfo.get_all_layouts()
-
- def get_defaults(self):
- """Return list of supported keyboard layouts for current language
-
- """
- lang_code = language_from_locale(self._settings.text.get_value())
- layouts = self._layouts_for_language(lang_code)
- if not layouts:
- country_code = country_from_locale(self._settings.text.get_value())
- layouts = self._layouts_for_country(country_code)
- if not layouts:
- layouts = ['us']
- return layouts
-
- def set_value(self, layout, is_default=False):
- super().set_value(layout, is_default)
- self._apply_layout_to_current_screen()
-
- def _layout_name(self, layout_code):
- id, display_name, short_name, xkb_layout, xkb_variant = \
- self.__xklinfo.get_layout_info(layout_code)
- return display_name
-
- def _layouts_split_names(self, layout_codes):
- layouts_names = {}
- for layout_code in layout_codes:
- layout_name = self._layout_name(layout_code)
- country_name, s, v = layout_name.partition(' (')
- if country_name not in layouts_names:
- layouts_names[country_name] = set([layout_code])
- else:
- layouts_names[country_name].add(layout_code)
- return layouts_names
-
- def _layouts_for_language(self, lang_code):
- """Return the list of available layouts for given language
- """
- layouts = []
- try:
- t_code = ln_iso639_tri(lang_code)
- except KeyError:
- t_code = lang_code
- if t_code == 'nno' or t_code == 'nob':
- t_code = 'nor'
-
- layouts = self.__xklinfo.get_layouts_for_language(t_code)
-
- if t_code == 'hrv':
- layouts.append('hr')
-
- if len(layouts) == 0:
- b_code = ln_iso639_2_T_to_B(t_code)
- logging.debug(
- "got no layout for ISO-639-2/T code %s, "
- "trying with ISO-639-2/B code %s",
- t_code, b_code)
-
- layouts = self.__xklinfo.get_layouts_for_language(b_code)
-
- logging.debug('got %d layouts for %s', len(layouts), lang_code)
- return layouts
-
- def _layouts_for_country(self, country):
- """Return the list of available layouts for given country
- """
- # XXX: it would be logical to use:
- # self.__xklinfo.get_layouts_for_language(country)
- # but it doesn't actually return the list of all layouts matching a
- # country.
- def country_filter(layout):
- cc = country.lower()
- return ((layout == cc)
- or ('+' in layout) and (layout.split('+')[0] == cc))
-
- layouts = list(filter(country_filter, self.get_all()))
-
- logging.debug('got %d layouts for %s', len(layouts), country)
- return layouts
-
- @staticmethod
- def _split_variant(layout_code):
- if '+' in layout_code:
- return layout_code.split('+')
- else:
- return (layout_code, None)
-
- def _filter_layouts(self, layouts, country, language):
- """Try to select the best layout in a layout list
- """
- if len(layouts) > 1:
- def variant_filter(layout):
- layout_name, layout_variant = self._split_variant(layout)
- return layout_variant is None
- filtered_layouts = list(filter(variant_filter, layouts))
- logging.debug("Filter by variant: %s", filtered_layouts)
- if len(filtered_layouts) > 0:
- layouts = filtered_layouts
- if len(layouts) > 1:
- def country_filter(layout):
- layout_name, layout_variant = self._split_variant(layout)
- return layout_variant == country.lower()
- filtered_layouts = list(filter(country_filter, layouts))
- logging.debug("Filter by country %s: %s", country,
- filtered_layouts)
- if len(filtered_layouts) > 0:
- layouts = filtered_layouts
- if len(layouts) > 1:
- def language_filter(layout):
- layout_name, layout_variant = self._split_variant(layout)
- return layout_variant == language
- filtered_layouts = list(filter(language_filter, layouts))
- logging.debug("Filter by language %s: %s", language,
- filtered_layouts)
- if len(filtered_layouts) > 0:
- layouts = filtered_layouts
- return layouts
-
- def set_default(self):
- """Sets the best default layout for the current locale
- """
- default_layout = False
-
- language = language_from_locale(self._settings.text.get_value())
- country = country_from_locale(self._settings.text.get_value())
-
- # First, build a list of layouts to consider for the language
- language_layouts = self._layouts_for_language(language)
- logging.debug("Language %s layouts: %s", language, language_layouts)
- country_layouts = self._layouts_for_country(country)
- logging.debug("Country %s layouts: %s", country, country_layouts)
- layouts = set(language_layouts).intersection(country_layouts)
- logging.debug("Intersection of language %s and country %s: %s",
- language, country, layouts)
- if not len(layouts) > 0:
- def country_filter(layout):
- layout_name, layout_variant = self._split_variant(layout)
- return layout_name == country.lower()
- layouts = list(filter(country_filter, language_layouts))
- logging.debug("Empty intersection of language and country, filter "
- "by country %s only: %s", country, layouts)
- if not len(layouts) > 0:
- def language_filter(layout):
- layout_name, layout_variant = self._split_variant(layout)
- return layout_name == language
- layouts = list(filter(language_filter, language_layouts))
- logging.debug("List still empty, filter by language %s only: %s",
- language, layouts)
- if not len(layouts) > 0:
- layouts = language_layouts
- logging.debug("List still empty, use all language %s layouts: %s",
- language, layouts)
-
- # Then, filter the list
- layouts = self._filter_layouts(layouts, country, language)
- if len(layouts) != 1:
- # Can't find a single result, build a new list for the country
- layouts = country_layouts
- logging.debug("Still not 1 layouts. Try again using all country "
- "%s layouts: %s", country, layouts)
- layouts = self._filter_layouts(layouts, country, language)
- if len(layouts) == 1:
- default_layout = layouts.pop()
- logging.debug("Selecting single matching layout %s",
- default_layout)
- elif len(layouts) > 1:
- default_layout = layouts.pop()
- logging.debug("No good layout, arbitrary using layout %s",
- default_layout)
- else:
- default_layout = 'us'
- logging.debug("Using us as fallback default layout")
- self.set_value(default_layout, is_default=True)
-
- def _apply_layout_to_current_screen(self):
- layout = self.get_value()
- logging.debug("layout=%s" % layout)
-
- settings = Gio.Settings('org.gnome.desktop.input-sources')
- settings.set_value('sources', GLib.Variant('a(ss)', [('xkb', layout)]))
-
-
-class LocalisationSettings(object):
- """Controller for localisation settings
-
- """
- def __init__(self, usermanager_loaded_cb=None, locale_selected_cb=None):
- self._usermanager_loaded_cb = usermanager_loaded_cb
- self._locale_selected_cb = locale_selected_cb
-
- self._act_user = None
- self.__actusermanager_loadedid = None
-
- self._system_locales_list = self.__get_langcodes()
- self._system_languages_dict = self.__fill_languages_dict(
- self._system_locales_list)
- self._system_formats_dict = self.__fill_formats_dict(
- self._system_locales_list)
-
- actusermanager = AccountsService.UserManager.get_default()
- self.__actusermanager_loadedid = actusermanager.connect(
- "notify::is-loaded", self.__on_usermanager_loaded)
-
- self.text = TextSetting(self)
- self.formats = FormatSetting(self)
- self.layout = LayoutSetting(self)
-
- def __del__(self):
- if self.__actusermanager_loadedid:
- self.__actusermanager.disconnect(self.__actusermanager_loadedid)
-
- def __get_langcodes(self):
- with open(tailsgreeter.config.language_codes_path, 'r') as f:
- return [line.rstrip('\n') for line in f.readlines()]
-
- def __on_usermanager_loaded(self, manager, pspec, data=None):
- logging.debug("Received AccountsManager signal is-loaded")
- act_user = manager.get_user(tailsgreeter.config.LUSER)
- if not act_user.is_loaded():
- raise RuntimeError("User manager for %s not loaded"
- % tailsgreeter.config.LUSER)
- self._act_user = act_user
- if self._usermanager_loaded_cb:
- self._usermanager_loaded_cb()
-
- def __fill_languages_dict(self, locale_codes):
- """assemble dictionary of language codes to corresponding locales list
-
- example {en: [en_US, en_GB], ...}"""
- languages_dict = {}
- for locale_code in locale_codes:
- lang_code = language_from_locale(locale_code)
- if lang_code not in languages_dict:
- languages_dict[lang_code] = []
- if locale_code not in languages_dict[lang_code]:
- languages_dict[lang_code].append(locale_code)
- return languages_dict
-
- def __fill_formats_dict(self, locale_codes):
- """assemble dictionary of country codes to corresponding locales list
-
- example {FR: [fr_FR, en_FR], ...}"""
- formats_dict = {}
- for locale_code in locale_codes:
- country_code = country_from_locale(locale_code)
- if country_code not in formats_dict:
- formats_dict[country_code] = []
- if locale_code not in formats_dict[country_code]:
- formats_dict[country_code].append(locale_code)
- return formats_dict
-
- def _apply_settings_to_upcoming_session(self):
- with open(tailsgreeter.config.locale_output_path, 'w') as f:
- os.chmod(tailsgreeter.config.locale_output_path, 0o600)
- if hasattr(self, "text"):
- f.write('TAILS_LOCALE_NAME=%s\n' % self.text.get_value())
- if hasattr(self, "formats"):
- f.write('TAILS_FORMATS=%s\n' % self.formats.get_value())
- if hasattr(self, "layout"):
- try:
- layout, variant = self.layout.get_value().split('+')
- except ValueError:
- layout = self.layout.get_value()
- variant = ''
- # XXX: use default value from /etc/default/keyboard
- f.write('TAILS_XKBMODEL=%s\n' % 'pc105')
- f.write('TAILS_XKBLAYOUT=%s\n' % layout)
- f.write('TAILS_XKBVARIANT=%s\n' % variant)
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/__init__.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/__init__.py
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py
new file mode 100644
index 0000000..02f03f6
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/formats.py
@@ -0,0 +1,128 @@
+import logging
+from typing import TYPE_CHECKING
+
+import gi
+
+from tailsgreeter.settings.localization import LocalizationSetting, language_from_locale, country_from_locale, \
+ countries_from_locales
+
+gi.require_version('GObject', '2.0')
+gi.require_version('GnomeDesktop', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GObject, GnomeDesktop, Gtk
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.localization_settings import LocalisationSettings
+
+
+class FormatsSetting(LocalizationSetting):
+ def __init__(self, settings_object: "LocalisationSettings"):
+ super().__init__(settings_object)
+ super().set_value('en_US', is_default=True)
+
+ def get_tree(self):
+ treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
+ GObject.TYPE_STRING) # name
+
+ format_codes = countries_from_locales(
+ self._settings.system_locales_list)
+ format_codes.sort(key=lambda x: self._country_name(x).lower())
+ logging.debug("format_codes=%s", format_codes)
+ for format_code in format_codes:
+ format_name = self._country_name(format_code)
+ if not format_name:
+ # Don't display languages without a name
+ continue
+ treeiter_format = treestore.append(parent=None)
+ treestore.set(treeiter_format,
+ 0, self.get_default_locale(format_code))
+ treestore.set(treeiter_format, 1, format_name)
+ locale_codes = sorted(
+ self.get_default_locales(format_code),
+ key=lambda x: self._locale_name(x).lower())
+ if len(locale_codes) > 1:
+ for locale_code in locale_codes:
+ treeiter_locale = treestore.append(
+ parent=treeiter_format)
+ treestore.set(treeiter_locale, 0, locale_code)
+ treestore.set(treeiter_locale, 1,
+ self._locale_name(locale_code))
+ return treestore
+
+ def get_name(self):
+ return self._locale_name(self.get_value())
+
+ def get_default_locales(self, country_code):
+ """Return available locales for given country
+
+ """
+ if country_code in self._settings.system_formats_dict:
+ return self._settings.system_formats_dict[country_code]
+
+ def get_default_locale(self, country_code=None):
+ """Return default locale for given country
+
+ Returns the 1st locale among:
+ - the locale whose country name matches country name
+ - the 1st locale for the language
+ - en_US
+ """
+ default_locales = self.get_default_locales(country_code)
+ if default_locales:
+ for locale_code in default_locales:
+ if (country_from_locale(locale_code).lower() ==
+ language_from_locale(locale_code)):
+ return locale_code
+ return default_locales[0]
+ else:
+ return 'en_US'
+
+ def _country_name(self, country_code):
+ default_locale = 'C'
+ local_locale = self.get_default_locale(country_code)
+ native_name = GnomeDesktop.get_country_from_code(
+ country_code, local_locale)
+ if not native_name:
+ return ""
+ localized_name = GnomeDesktop.get_country_from_code(
+ country_code, default_locale)
+ if native_name == localized_name:
+ return native_name
+ else:
+ return "{native} ({localized})".format(
+ native=native_name, localized=localized_name)
+
+ @staticmethod
+ def _locale_name(locale_code):
+ lang_code = language_from_locale(locale_code)
+ country_code = country_from_locale(locale_code)
+ language_name_locale = GnomeDesktop.get_language_from_code(lang_code)
+ language_name_native = GnomeDesktop.get_language_from_code(
+ lang_code, locale_code)
+ country_name_locale = GnomeDesktop.get_country_from_code(country_code)
+ country_name_native = GnomeDesktop.get_country_from_code(
+ country_code, locale_code)
+ try:
+ if (language_name_native == language_name_locale and
+ country_name_native == country_name_locale):
+ return "{country} - {language}".format(
+ language=language_name_native.capitalize(),
+ country=country_name_native)
+ else:
+ return "{country} - {language} " \
+ "({local_country} - {local_language})".format(
+ language=language_name_native.capitalize(),
+ country=country_name_native,
+ local_language=language_name_locale.capitalize(),
+ local_country=country_name_locale)
+ except AttributeError:
+ return locale_code
+
+ def set_default(self):
+ """Set default format for current language
+
+ Select the same locale for formats that the language
+ """
+ default_format = self._settings.language.get_value()
+ logging.debug("setting default formats to %s", default_format)
+ self.set_value(default_format, is_default=True)
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py
new file mode 100644
index 0000000..65de965
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/keyboard.py
@@ -0,0 +1,230 @@
+import logging
+import gi
+
+from tailsgreeter.settings.localization import LocalizationSetting, ln_iso639_tri, \
+ ln_iso639_2_T_to_B, language_from_locale, country_from_locale
+
+gi.require_version('Gio', '2.0')
+gi.require_version('GLib', '2.0')
+gi.require_version('GnomeDesktop', '3.0')
+gi.require_version('GObject', '2.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gio, GLib, GnomeDesktop, GObject, Gtk
+
+
+class KeyboardSetting(LocalizationSetting):
+
+ def __init__(self, settings_object):
+ super().__init__(settings_object)
+ self.xkbinfo = GnomeDesktop.XkbInfo()
+ super().set_value('us', is_default=True)
+
+ def get_tree(self, layout_codes=None):
+ if not layout_codes:
+ layout_codes = self.get_all()
+
+ treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
+ GObject.TYPE_STRING) # name
+ layouts = self._layouts_split_names(layout_codes)
+ for group_name in sorted(layouts.keys()):
+ layout_codes = sorted(
+ layouts[group_name],
+ key=lambda x: self._layout_name(x).lower())
+ treeiter_group = treestore.append(parent=None)
+ # we fill the title with the 1st layout of the group
+ treestore.set(treeiter_group, 0, layout_codes[0])
+ treestore.set(treeiter_group, 1, group_name)
+ if len(layout_codes) > 1:
+ for layout_code in layout_codes:
+ treeiter_layout = treestore.append(parent=treeiter_group)
+ treestore.set(treeiter_layout, 0, layout_code)
+ treestore.set(treeiter_layout, 1,
+ self._layout_name(layout_code))
+ return treestore
+
+ def get_name(self):
+ return self._layout_name(self.get_value())
+
+ def get_all(self):
+ """Return a list of all keyboard layout codes
+
+ """
+ return self.xkbinfo.get_all_layouts()
+
+ def get_defaults(self):
+ """Return list of supported keyboard layouts for current language
+
+ """
+ lang_code = language_from_locale(self._settings.language.get_value())
+ layouts = self._layouts_for_language(lang_code)
+ if not layouts:
+ country_code = country_from_locale(self._settings.language.get_value())
+ layouts = self._layouts_for_country(country_code)
+ if not layouts:
+ layouts = ['us']
+ return layouts
+
+ def set_value(self, layout, is_default=False):
+ super().set_value(layout, is_default)
+ self._apply_layout_to_current_screen()
+
+ def _layout_name(self, layout_code):
+ layout_exists, display_name, short_name, xkb_layout, xkb_variant = \
+ self.xkbinfo.get_layout_info(layout_code)
+ if not layout_exists:
+ logging.warning("Layout code '%s' does not exist", layout_code)
+ return display_name
+
+ def _layouts_split_names(self, layout_codes):
+ layouts_names = {}
+ for layout_code in layout_codes:
+ layout_name = self._layout_name(layout_code)
+ country_name, s, v = layout_name.partition(' (')
+ if country_name not in layouts_names:
+ layouts_names[country_name] = {layout_code}
+ else:
+ layouts_names[country_name].add(layout_code)
+ return layouts_names
+
+ def _layouts_for_language(self, lang_code):
+ """Return the list of available layouts for given language
+ """
+ try:
+ t_code = ln_iso639_tri(lang_code)
+ except KeyError:
+ t_code = lang_code
+ if t_code == 'nno' or t_code == 'nob':
+ t_code = 'nor'
+
+ layouts = self.xkbinfo.get_layouts_for_language(t_code)
+
+ if t_code == 'hrv':
+ layouts.append('hr')
+
+ if len(layouts) == 0:
+ b_code = ln_iso639_2_T_to_B(t_code)
+ logging.debug(
+ "got no layout for ISO-639-2/T code %s, "
+ "trying with ISO-639-2/B code %s",
+ t_code, b_code)
+
+ layouts = self.xkbinfo.get_layouts_for_language(b_code)
+
+ logging.debug('got %d layouts for %s', len(layouts), lang_code)
+ return layouts
+
+ def _layouts_for_country(self, country):
+ """Return the list of available layouts for given country
+ """
+ # XXX: it would be logical to use:
+ # self.__xklinfo.get_layouts_for_language(country)
+ # but it doesn't actually return the list of all layouts matching a
+ # country.
+ def country_filter(layout):
+ cc = country.lower()
+ return ((layout == cc)
+ or ('+' in layout) and (layout.split('+')[0] == cc))
+
+ layouts = list(filter(country_filter, self.get_all()))
+
+ logging.debug('got %d layouts for %s', len(layouts), country)
+ return layouts
+
+ @staticmethod
+ def _split_variant(layout_code):
+ if '+' in layout_code:
+ return layout_code.split('+')
+ else:
+ return layout_code, None
+
+ def _filter_layouts(self, layouts, country, language):
+ """Try to select the best layout in a layout list
+ """
+ if len(layouts) > 1:
+ def variant_filter(layout):
+ layout_name, layout_variant = self._split_variant(layout)
+ return layout_variant is None
+ filtered_layouts = list(filter(variant_filter, layouts))
+ logging.debug("Filter by variant: %s", filtered_layouts)
+ if len(filtered_layouts) > 0:
+ layouts = filtered_layouts
+ if len(layouts) > 1:
+ def country_filter(layout):
+ layout_name, layout_variant = self._split_variant(layout)
+ return layout_variant == country.lower()
+ filtered_layouts = list(filter(country_filter, layouts))
+ logging.debug("Filter by country %s: %s", country,
+ filtered_layouts)
+ if len(filtered_layouts) > 0:
+ layouts = filtered_layouts
+ if len(layouts) > 1:
+ def language_filter(layout):
+ layout_name, layout_variant = self._split_variant(layout)
+ return layout_variant == language
+ filtered_layouts = list(filter(language_filter, layouts))
+ logging.debug("Filter by language %s: %s", language,
+ filtered_layouts)
+ if len(filtered_layouts) > 0:
+ layouts = filtered_layouts
+ return layouts
+
+ def set_default(self):
+ """Sets the best default layout for the current locale
+ """
+ language = language_from_locale(self._settings.language.get_value())
+ country = country_from_locale(self._settings.language.get_value())
+
+ # First, build a list of layouts to consider for the language
+ language_layouts = self._layouts_for_language(language)
+ logging.debug("Language %s layouts: %s", language, language_layouts)
+ country_layouts = self._layouts_for_country(country)
+ logging.debug("Country %s layouts: %s", country, country_layouts)
+ layouts = set(language_layouts).intersection(country_layouts)
+ logging.debug("Intersection of language %s and country %s: %s",
+ language, country, layouts)
+ if not len(layouts) > 0:
+ def country_filter(layout):
+ layout_name, layout_variant = self._split_variant(layout)
+ return layout_name == country.lower()
+ layouts = list(filter(country_filter, language_layouts))
+ logging.debug("Empty intersection of language and country, filter "
+ "by country %s only: %s", country, layouts)
+ if not len(layouts) > 0:
+ def language_filter(layout):
+ layout_name, layout_variant = self._split_variant(layout)
+ return layout_name == language
+ layouts = list(filter(language_filter, language_layouts))
+ logging.debug("List still empty, filter by language %s only: %s",
+ language, layouts)
+ if not len(layouts) > 0:
+ layouts = language_layouts
+ logging.debug("List still empty, use all language %s layouts: %s",
+ language, layouts)
+
+ # Then, filter the list
+ layouts = self._filter_layouts(layouts, country, language)
+ if len(layouts) != 1:
+ # Can't find a single result, build a new list for the country
+ layouts = country_layouts
+ logging.debug("Still not 1 layouts. Try again using all country "
+ "%s layouts: %s", country, layouts)
+ layouts = self._filter_layouts(layouts, country, language)
+ if len(layouts) == 1:
+ default_layout = layouts.pop()
+ logging.debug("Selecting single matching layout %s",
+ default_layout)
+ elif len(layouts) > 1:
+ default_layout = layouts.pop()
+ logging.debug("No good layout, arbitrary using layout %s",
+ default_layout)
+ else:
+ default_layout = 'us'
+ logging.debug("Using us as fallback default layout")
+ self.set_value(default_layout, is_default=True)
+
+ def _apply_layout_to_current_screen(self):
+ layout = self.get_value()
+ logging.debug("layout=%s", layout)
+
+ settings = Gio.Settings('org.gnome.desktop.input-sources')
+ settings.set_value('sources', GLib.Variant('a(ss)', [('xkb', layout)]))
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py
new file mode 100644
index 0000000..f2f9409
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/language.py
@@ -0,0 +1,153 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2012-2019 Tails developers <tails@boum.org>
+# Copyright 2011 Max <govnototalitarizm@gmail.com>
+# Copyright 2011 Martin Owens
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+import gi
+import logging
+import locale
+
+from tailsgreeter.settings.localization import LocalizationSetting, \
+ language_from_locale, languages_from_locales, country_from_locale
+
+gi.require_version('GLib', '2.0')
+gi.require_version('GObject', '2.0')
+gi.require_version('GnomeDesktop', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GLib, GObject, GnomeDesktop, Gtk
+
+
+class LanguageSetting(LocalizationSetting):
+
+ def __init__(self, settings_object):
+ super().__init__(settings_object)
+ super().set_value('en_US', is_default=True)
+
+ def get_tree(self):
+ treestore = Gtk.TreeStore(GObject.TYPE_STRING, # id
+ GObject.TYPE_STRING) # name
+
+ lang_codes = languages_from_locales(
+ self._settings.system_locales_list)
+ lang_codes.sort(key=lambda x: self._language_name(x).lower())
+ for lang_code in lang_codes:
+ language_name = self._language_name(lang_code)
+ if not language_name:
+ # Don't display languages without a name
+ continue
+ treeiter_language = treestore.append(parent=None)
+ treestore.set(treeiter_language,
+ 0, self.get_default_locale(lang_code))
+ treestore.set(treeiter_language, 1, language_name)
+ locale_codes = sorted(
+ self.get_default_locales(lang_code),
+ key=lambda x: self._locale_name(x).lower())
+ if len(locale_codes) > 1:
+ for locale_code in locale_codes:
+ treeiter_locale = treestore.append(
+ parent=treeiter_language)
+ treestore.set(treeiter_locale, 0, locale_code)
+ treestore.set(treeiter_locale, 1,
+ self._locale_name(locale_code))
+ return treestore
+
+ def get_name(self):
+ return self._locale_name(self.get_value())
+
+ def get_default_locales(self, lang_code):
+ """Return available locales for given language
+
+ """
+ if lang_code in self._settings._system_languages_dict:
+ return self._settings._system_languages_dict[lang_code]
+
+ def get_default_locale(self, lang_code=None):
+ """Return default locale for given language
+
+ Returns the 1st locale among:
+ - the locale whose country name matches language name
+ - the 1st locale for the language
+ - en_US
+ """
+ default_locales = self.get_default_locales(lang_code)
+ if default_locales:
+ for locale_code in default_locales:
+ if (country_from_locale(locale_code).lower() ==
+ language_from_locale(locale_code)):
+ return locale_code
+ return default_locales[0]
+ else:
+ return 'en_US'
+
+ def set_value(self, locale, is_default=False):
+ super().set_value(locale, is_default)
+ self.__apply_locale()
+ self._settings.formats.set_default_if_needed() # XXX: notify
+ self._settings.keyboard.set_default_if_needed() # XXX: notify
+ if self._settings._locale_selected_cb:
+ self._settings._locale_selected_cb(locale)
+
+ def _language_name(self, lang_code):
+ default_locale = 'C'
+ local_locale = self.get_default_locale(lang_code)
+ try:
+ native_name = GnomeDesktop.get_language_from_code(
+ lang_code, local_locale).capitalize()
+ except AttributeError:
+ return ""
+ localized_name = GnomeDesktop.get_language_from_code(
+ lang_code, default_locale).capitalize()
+ if native_name == localized_name:
+ return native_name
+ else:
+ return "{native} ({localized})".format(
+ native=native_name, localized=localized_name)
+
+ def _locale_name(self, locale_code):
+ lang_code = language_from_locale(locale_code)
+ country_code = country_from_locale(locale_code)
+ language_name_locale = GnomeDesktop.get_language_from_code(lang_code)
+ language_name_native = GnomeDesktop.get_language_from_code(
+ lang_code, locale_code)
+ country_name_locale = GnomeDesktop.get_country_from_code(country_code)
+ country_name_native = GnomeDesktop.get_country_from_code(
+ country_code, locale_code)
+ try:
+ if (language_name_native == language_name_locale and
+ country_name_native == country_name_locale):
+ return "{language} - {country}".format(
+ language=language_name_native.capitalize(),
+ country=country_name_native)
+ else:
+ return "{language} - {country} " \
+ "({local_language} - {local_country})".format(
+ language=language_name_native.capitalize(),
+ country=country_name_native,
+ local_language=language_name_locale.capitalize(),
+ local_country=country_name_locale)
+ except AttributeError:
+ return locale_code
+
+ def __apply_locale(self):
+ locale_code = locale.normalize(
+ self.get_value() + '.' + locale.getpreferredencoding())
+ logging.debug("Setting session language to %s", locale_code)
+ if self._settings._act_user:
+ GLib.idle_add(
+ lambda: self._settings._act_user.set_language(locale_code))
+ else:
+ logging.warning("AccountsManager not ready")
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py
new file mode 100644
index 0000000..665d551
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization.py
@@ -0,0 +1,114 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2012-2019 Tails developers <tails@boum.org>
+# Copyright 2011 Max <govnototalitarizm@gmail.com>
+# Copyright 2011 Martin Owens
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+import gi
+import pycountry
+from typing import TYPE_CHECKING
+
+gi.require_version('GObject', '2.0')
+from gi.repository import GObject
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.localization_settings import LocalisationSettings
+
+
+class LocalizationSetting(GObject.Object):
+
+ value = GObject.property(type=str)
+ is_default = GObject.property(type=bool, default=True)
+
+ def __init__(self, settings_object: "LocalisationSettings"):
+ super().__init__()
+ self._settings = settings_object
+
+ def get_value(self):
+ return self.value
+
+ # is_default will be used by subclasses to register default value
+ def set_value(self, value, is_default=False):
+ self.value = value
+ if not is_default:
+ self.is_default = False
+ self._settings.apply_settings_to_upcoming_session()
+
+ def get_name(self):
+ raise NotImplementedError
+
+ def get_tree(self):
+ raise NotImplementedError
+
+ def set_default(self):
+ raise NotImplementedError
+
+ def set_default_if_needed(self):
+ """Update default value if it was not user choosen"""
+ if self.is_default:
+ self.set_default()
+
+
+def ln_iso639_tri(ln_CC):
+ """get iso639 3-letter code from a language code
+
+ example: en -> eng"""
+ return pycountry.languages.get(
+ alpha2=language_from_locale(ln_CC)).terminology
+
+
+def ln_iso639_2_T_to_B(lng):
+ """Convert a ISO-639-2/T code (e.g. deu for German) to a 639-2/B one
+ (e.g. ger for German)"""
+ return pycountry.languages.get(terminology=lng).bibliographic
+
+
+def language_from_locale(locale):
+ """Obtain the language code from a locale code
+
+ example: fr_FR -> fr"""
+ return locale.split('_')[0]
+
+
+def languages_from_locales(locales):
+ """Obtain a language code list from a locale code list
+
+ example: [fr_FR, en_GB] -> [fr, en]"""
+ language_codes = []
+ for l in locales:
+ language_code = language_from_locale(l)
+ if language_code not in language_codes:
+ language_codes.append(language_code)
+ return language_codes
+
+
+def country_from_locale(locale):
+ """Obtain the country code from a locale code
+
+ example: fr_FR -> FR"""
+ return locale.split('_')[1]
+
+
+def countries_from_locales(locales):
+ """Obtain a country code list from a locale code list
+
+ example: [fr_FR, en_GB] -> [FR, GB]"""
+ country_codes = []
+ for l in locales:
+ country_code = country_from_locale(l)
+ if country_code not in country_codes:
+ country_codes.append(country_code)
+ return country_codes
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py
new file mode 100644
index 0000000..0f15c20
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/localization_settings.py
@@ -0,0 +1,103 @@
+import gi
+import logging
+import os
+
+import tailsgreeter.config
+from tailsgreeter.settings.localization import language_from_locale, country_from_locale
+from tailsgreeter.settings.formats import FormatsSetting
+from tailsgreeter.settings.keyboard import KeyboardSetting
+from tailsgreeter.settings.language import LanguageSetting
+
+gi.require_version('AccountsService', '1.0')
+from gi.repository import AccountsService
+
+
+class LocalisationSettings(object):
+ """Controller for localisation settings
+
+ """
+ def __init__(self, usermanager_loaded_cb=None, locale_selected_cb=None):
+ self._usermanager_loaded_cb = usermanager_loaded_cb
+ self._locale_selected_cb = locale_selected_cb
+
+ self._act_user = None
+ self._actusermanager_loadedid = None
+
+ self.system_locales_list = self.__get_langcodes()
+ self._system_languages_dict = self.__fill_languages_dict(
+ self.system_locales_list)
+ self.system_formats_dict = self.__fill_formats_dict(
+ self.system_locales_list)
+
+ self._actusermanager = AccountsService.UserManager.get_default()
+ self._actusermanager_loadedid = self._actusermanager.connect(
+ "notify::is-loaded", self.__on_usermanager_loaded)
+
+ self.language = LanguageSetting(self)
+ self.keyboard = KeyboardSetting(self)
+ self.formats = FormatsSetting(self)
+
+ def __del__(self):
+ if self._actusermanager_loadedid:
+ self._actusermanager.disconnect(self._actusermanager_loadedid)
+
+ @staticmethod
+ def __get_langcodes():
+ with open(tailsgreeter.config.language_codes_path, 'r') as f:
+ return [line.rstrip('\n') for line in f.readlines()]
+
+ def __on_usermanager_loaded(self, manager, pspec, data=None):
+ logging.debug("Received AccountsManager signal is-loaded")
+ act_user = manager.get_user(tailsgreeter.config.LUSER)
+ if not act_user.is_loaded():
+ raise RuntimeError("User manager for %s not loaded"
+ % tailsgreeter.config.LUSER)
+ self._act_user = act_user
+ if self._usermanager_loaded_cb:
+ self._usermanager_loaded_cb()
+
+ @staticmethod
+ def __fill_languages_dict(locale_codes):
+ """assemble dictionary of language codes to corresponding locales list
+
+ example {en: [en_US, en_GB], ...}"""
+ languages_dict = {}
+ for locale_code in locale_codes:
+ lang_code = language_from_locale(locale_code)
+ if lang_code not in languages_dict:
+ languages_dict[lang_code] = []
+ if locale_code not in languages_dict[lang_code]:
+ languages_dict[lang_code].append(locale_code)
+ return languages_dict
+
+ @staticmethod
+ def __fill_formats_dict(locale_codes):
+ """assemble dictionary of country codes to corresponding locales list
+
+ example {FR: [fr_FR, en_FR], ...}"""
+ formats_dict = {}
+ for locale_code in locale_codes:
+ country_code = country_from_locale(locale_code)
+ if country_code not in formats_dict:
+ formats_dict[country_code] = []
+ if locale_code not in formats_dict[country_code]:
+ formats_dict[country_code].append(locale_code)
+ return formats_dict
+
+ def apply_settings_to_upcoming_session(self):
+ with open(tailsgreeter.config.locale_output_path, 'w') as f:
+ os.chmod(tailsgreeter.config.locale_output_path, 0o600)
+ if hasattr(self, "text"):
+ f.write('TAILS_LOCALE_NAME=%s\n' % self.language.get_value())
+ if hasattr(self, "formats"):
+ f.write('TAILS_FORMATS=%s\n' % self.formats.get_value())
+ if hasattr(self, "layout"):
+ try:
+ layout, variant = self.keyboard.get_value().split('+')
+ except ValueError:
+ layout = self.keyboard.get_value()
+ variant = ''
+ # XXX: use default value from /etc/default/keyboard
+ f.write('TAILS_XKBMODEL=%s\n' % 'pc105')
+ f.write('TAILS_XKBLAYOUT=%s\n' % layout)
+ f.write('TAILS_XKBVARIANT=%s\n' % variant)
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/persistence.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py
index cabefd6..e9bf9a2 100644
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/persistence.py
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py
@@ -77,7 +77,8 @@ class PersistenceSettings(object):
logging.exception(e)
return False
- def list_containers(self):
+ @staticmethod
+ def list_containers():
"""Returns a list of persistence containers we might want to unlock."""
args = [
"/usr/bin/sudo", "-n", "/usr/local/sbin/live-persist",
@@ -143,7 +144,8 @@ class PersistenceSettings(object):
"{stdout}\n{stderr}")
)
- def setup_persistence(self, cleartext_device, readonly):
+ @staticmethod
+ def setup_persistence(cleartext_device, readonly):
args = ["/usr/bin/sudo", "-n", "/usr/local/sbin/live-persist"]
if readonly:
args.append('--read-only')
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/physicalsecurity.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py
index 75b744b..75b744b 100644
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/physicalsecurity.py
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/physicalsecurity.py
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/rootaccess.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py
index 057f8be..057f8be 100644
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/rootaccess.py
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/rootaccess.py
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py
new file mode 100644
index 0000000..39c0988
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/translatable_window.py
@@ -0,0 +1,135 @@
+import gettext
+import logging
+
+from gi.repository import Gtk
+
+import tailsgreeter.config
+from tailsgreeter import TRANSLATION_DOMAIN
+
+
+class TranslatableWindow(object):
+ """Interface providing functions to translate a window on the fly
+ """
+ retain_focus = True
+ registered_windows = []
+
+ def __init__(self, window):
+ self.window_ = window
+ self.translation = gettext.translation(
+ TRANSLATION_DOMAIN,
+ tailsgreeter.config.locales_path,
+ fallback=True
+ )
+
+ self.containers = []
+ self.labels = {}
+ self.placeholder_texts = {}
+ self.titles = {}
+ self.tooltips = {}
+
+ TranslatableWindow.registered_windows.append(window)
+
+ @staticmethod
+ def get_locale_direction(lang):
+ """Return Gtk.TextDirection for lang
+
+ This method in basically the same as Gtk.get_locale_direction
+ (gtk_get_locale_direction in gtk/gtkmain.c), but it accepts a lang
+ parameter rather than using current locale.
+ """
+ gtk_translation = gettext.translation("gtk30",
+ languages=[str(lang)],
+ fallback=True)
+ logging.debug("%s has GTK translation: %s", lang, gtk_translation)
+ # Translators: please do not translate this string (it is read from
+ # Gtk translation)
+ default_dir = gtk_translation.gettext("default:LTR")
+ logging.debug("translation for direction is %s", default_dir)
+ if default_dir == "default:RTL":
+ logging.debug("%s is RTL", lang)
+ return Gtk.TextDirection.RTL
+ else:
+ return Gtk.TextDirection.LTR
+
+ def store_translations(self, widget):
+ """Store the elements that should be localised inside widget
+
+ Go through all children of widget and store the translations
+ of labels, tooltips and titles and the containers whose text direction
+ should be updated when switching between LTR and RTL.
+
+ This method should be called once the widgets are created"""
+ if not isinstance(widget, Gtk.Widget):
+ logging.debug("%s is not a Gtk.Widget", widget)
+ return None
+ if isinstance(widget, Gtk.Label) or isinstance(widget, Gtk.Button):
+ if widget not in self.labels:
+ logging.debug("Storing translation for label/button '%s'", widget.get_label())
+ self.labels[widget] = widget.get_label()
+ # Wrap set_label to get notified about string changes
+ widget.original_set_label = widget.set_label
+
+ def wrapped_set_label(text):
+ self.labels[widget] = text
+ widget.original_set_label(self.gettext(text))
+ widget.set_label = wrapped_set_label
+ elif isinstance(widget, Gtk.Entry):
+ if widget not in self.placeholder_texts:
+ logging.debug("Storing translation for entry '%s'", widget.get_placeholder_text())
+ self.placeholder_texts[widget] = widget.get_placeholder_text()
+ elif isinstance(widget, Gtk.Container):
+ logging.debug("Handling container '%s'", widget.get_name())
+ self.containers.append(widget)
+ if ((isinstance(widget, Gtk.HeaderBar) or
+ isinstance(widget, Gtk.Window)) and
+ widget not in self.titles and
+ widget.get_title()):
+ self.titles[widget] = widget.get_title()
+ for child in widget.get_children():
+ self.store_translations(child)
+ else:
+ logging.debug("W: unhandled widget: %s", widget)
+ if widget.get_has_tooltip():
+ if widget not in self.tooltips:
+ self.tooltips[widget] = widget.get_tooltip_text()
+
+ def gettext(self, text):
+ """Return text, translated if possible"""
+ if self.translation and text:
+ text = self.translation.gettext(text)
+ return text
+
+ def translate_to(self, lang):
+ """Translate registered widgets on the fly
+
+ Loop through widgets registered with store_translations and translate
+ them on the fly"""
+ logging.debug("translating %s to %s", self, lang)
+ try:
+ self.translation = gettext.translation(
+ TRANSLATION_DOMAIN,
+ tailsgreeter.config.locales_path, [str(lang)])
+ except IOError:
+ self.translation = None
+
+ text_direction = self.get_locale_direction(lang)
+ for container in self.containers:
+ container.set_default_direction(text_direction)
+ for widget, label in self.labels.items():
+ if label:
+ widget.original_set_label(self.gettext(label))
+ for widget in self.placeholder_texts.keys():
+ widget.set_placeholder_text(self.gettext(self.placeholder_texts[widget]))
+ for widget in self.titles.keys():
+ widget.set_title(self.gettext(self.titles[widget]))
+ for widget in self.tooltips.keys():
+ widget.set_tooltip_markup(self.gettext(self.tooltips[widget]))
+ if (self.window_.get_sensitive() and
+ self.window_.get_visible() and
+ self.retain_focus):
+ self.window_.present()
+
+ @staticmethod
+ def translate_all(lang):
+ for widget in TranslatableWindow.registered_windows:
+ widget.translate_to(lang)
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/__init__.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/__init__.py
new file mode 100644
index 0000000..33e4895
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/__init__.py
@@ -0,0 +1,3 @@
+# Mark translatable strings, but don't actually translate them, as we
+# delegate this to TranslatableWindow that handles on-the-fly language changes
+_ = lambda text: text
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py
new file mode 100644
index 0000000..2c85f84
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py
@@ -0,0 +1,142 @@
+import logging
+from gettext import gettext
+import gi
+from typing import TYPE_CHECKING
+
+from tailsgreeter.translatable_window import TranslatableWindow
+
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk, Gtk
+
+if TYPE_CHECKING:
+ from tailsgreeter.ui.settings_collection import GreeterSettingsCollection
+
+_ = gettext
+
+PREFERRED_WIDTH = 400
+
+
+class AddSettingsDialog(Gtk.Dialog, TranslatableWindow):
+ def __init__(self, builder, settings: "GreeterSettingsCollection"):
+ Gtk.Dialog.__init__(self, use_header_bar=True)
+ TranslatableWindow.__init__(self, self)
+ self.settings = settings
+ self.listbox = builder.get_object('listbox_add_setting')
+
+ for setting in self.settings.additional_settings:
+ logging.debug("Adding '%s' to additional settings listbox", setting.id)
+ self.listbox.add(setting.listboxrow)
+
+ self.set_transient_for(self)
+ self.set_title(_("Additional Settings"))
+ self.set_default_size(-1, PREFERRED_WIDTH)
+
+ sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
+
+ accelgroup = Gtk.AccelGroup()
+ self.add_accel_group(accelgroup)
+
+ self.button_cancel = self.add_button(_("Cancel"),
+ Gtk.ResponseType.CANCEL)
+ accelgroup.connect(Gdk.KEY_Escape, 0, 0,
+ self.cb_accelgroup_cancel_activated)
+ sizegroup.add_widget(self.button_cancel)
+
+ self.button_add = self.add_button(_("Add"), Gtk.ResponseType.YES)
+ Gtk.StyleContext.add_class(self.button_add.get_style_context(),
+ 'suggested-action')
+ sizegroup.add_widget(self.button_add)
+ accelgroup.connect(Gdk.KEY_Return, 0, 0,
+ self.cb_accelgroup_add_activated)
+ self.button_add.set_visible(False)
+
+ self.button_back = Gtk.Button.new_with_label(_("Back"))
+ self.button_back.set_visible(False)
+ self.button_back.connect('clicked', self.cb_button_back_clicked, None)
+ sizegroup.add_widget(self.button_back)
+ accelgroup.connect(Gdk.KEY_Back, 0, 0,
+ self.cb_accelgroup_back_activated)
+ # These key bindings are copied from Firefox, and are the same with
+ # right-to-left languages.
+ accelgroup.connect(Gdk.KEY_Left, Gdk.ModifierType.MOD1_MASK, 0,
+ self.cb_accelgroup_back_activated)
+ accelgroup.connect(Gdk.KEY_KP_Left, Gdk.ModifierType.MOD1_MASK, 0,
+ self.cb_accelgroup_back_activated)
+ self.get_header_bar().pack_end(self.button_back)
+
+ self.stack = Gtk.Stack()
+ self.stack.add_named(self.listbox, "setting-type")
+ self.listbox.set_valign(Gtk.Align.FILL)
+ self.listbox.set_vexpand(True)
+ self.stack.set_visible(True)
+ # XXX: is SLIDE_LEFT_RIGHT automatically inversed in RTL mode?
+ self.stack.set_transition_type(
+ Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
+ self.get_content_area().add(self.stack)
+
+ # Store translations
+ self.store_translations(self)
+ for setting in self.settings.additional_settings:
+ self.store_translations(setting.box)
+
+ def go_back(self):
+ self.stack.set_visible_child_name('setting-type')
+ self.button_back.set_visible(False)
+ self.button_add.set_visible(False)
+ self.stack.remove(self.stack.get_child_by_name('setting-details'))
+
+ def listbox_focus(self):
+ # Workaround autoselection of 1st item on focus
+ self.listbox.unselect_all()
+
+ def listbox_row_activated(self, row) -> bool:
+ if not row: # this happens when the row gets unselected
+ return False
+
+ # Show the selected setting
+ id_ = self.settings.id_from_row(row)
+ setting = self.settings.additional_settings[id_]
+ self.stack.add_named(setting.box, 'setting-details')
+ self.stack.set_visible_child_name('setting-details')
+ self.button_add.set_sensitive(True)
+ self.button_back.set_visible(True)
+ self.button_add.set_visible(True)
+ setting.on_opened_in_dialog()
+
+ def run(self, id=None) -> int:
+ # Set the dialog attribute for the additional settings.
+ # This is required in order to allow the interactions with the
+ # setting's UI elements to change the dialog UI elements, for
+ # example the sensitivity of the "Add" button.
+ for setting in self.settings.additional_settings:
+ setting.dialog = self
+
+ if id:
+ row = self.settings[id].listboxrow
+ row.emit("activate")
+ else:
+ self.stack.set_visible_child_name('setting-type')
+ self.button_back.set_visible(False)
+ self.button_add.set_visible(False)
+ return super().run()
+
+ def cb_accelgroup_add_activated(self, accel_group, accelerable, keyval,
+ modifier, user_data=None):
+ if self.button_add.get_visible() and self.button_add.get_sensitive():
+ self.response(Gtk.ResponseType.YES)
+ return False
+
+ def cb_accelgroup_back_activated(self, accel_group, accelerable, keyval,
+ modifier, user_data=None):
+ self.go_back()
+ return False
+
+ def cb_accelgroup_cancel_activated(self, accel_group, accelerable, keyval,
+ modifier, user_data=None):
+ self.response(Gtk.ResponseType.CANCEL)
+ return True # disable the default callbacks that destroys the dialog
+
+ def cb_button_back_clicked(self, widget, user_data=None):
+ self.go_back()
+ return False
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py
new file mode 100644
index 0000000..c7c471c
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py
@@ -0,0 +1,275 @@
+import gi
+
+from tailsgreeter import TRANSLATION_DOMAIN
+import tailsgreeter.config
+import tailsgreeter.utils
+from tailsgreeter.ui import _
+from tailsgreeter.ui.setting import GreeterSetting
+from tailsgreeter.ui.popover import Popover
+from typing import TYPE_CHECKING
+
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk, Gtk
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.rootaccess import RootAccessSettings
+ from tailsgreeter.settings.physicalsecurity import PhysicalSecuritySettings
+
+ADDITIONAL_SETTINGS_UI_FILE = "additional_settings.ui"
+
+
+class AdditionalSetting(GreeterSetting):
+ def __init__(self):
+ super().__init__()
+ self.dialog = None
+
+ self.builder = Gtk.Builder()
+ self.builder.set_translation_domain(TRANSLATION_DOMAIN)
+ self.builder.add_from_file(tailsgreeter.config.data_path + ADDITIONAL_SETTINGS_UI_FILE)
+ self.box = self.builder.get_object("box_{}_popover".format(self.id))
+
+ def build_popover(self):
+ self.popover = Popover(self.listboxrow, self.box)
+
+ def close_window(self, response: Gtk.ResponseType):
+ if self.has_popover() and self.popover.is_open():
+ self.popover.close(response)
+ else:
+ self.dialog.response(response)
+
+ def apply(self):
+ pass
+
+ def on_opened_in_dialog(self):
+ pass
+
+
+class AdminSetting(AdditionalSetting):
+ @property
+ def id(self) -> str:
+ return "admin"
+
+ @property
+ def title(self) -> str:
+ return _("_Administration Password")
+
+ @property
+ def icon_name(self) -> str:
+ return "tails-admin"
+
+ @property
+ def value_for_display(self) -> str:
+ return get_on_off_string(self.password, default=None)
+
+ def update_check_icon(self):
+ password = self.password_entry.get_text()
+ password_verify = self.password_verify_entry.get_text()
+ if not password_verify:
+ icon = None
+ elif password_verify == password:
+ icon = 'emblem-ok-symbolic'
+ else:
+ icon = 'dialog-warning-symbolic'
+ self.password_verify_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, icon)
+
+ def on_opened_in_dialog(self):
+ self.update_check_icon()
+ self.dialog.button_add.set_sensitive(self.passwords_match())
+ self.password_entry.grab_focus()
+
+ def __init__(self, root_access_setting: "RootAccessSettings"):
+ self._root_access_setting = root_access_setting
+ self.password = None
+ super().__init__()
+ self.accel_key = Gdk.KEY_a
+
+ self.password_entry = self.builder.get_object('entry_admin_password')
+ self.password_entry.connect('changed', self.cb_entry_admin_changed)
+ self.password_entry.connect('activate', self.cb_entry_admin_activate)
+ self.password_verify_entry = self.builder.get_object('entry_admin_verify')
+ self.password_verify_entry.connect('changed', self.cb_entry_admin_changed)
+ self.password_verify_entry.connect('activate', self.cb_entry_admin_activate)
+ self.box_admin_password = self.builder.get_object('box_admin_password')
+ self.box_admin_verify = self.builder.get_object('box_admin_verify')
+ self.button_admin_disable = self.builder.get_object('button_admin_disable')
+ self.button_admin_disable.connect('clicked', self.cb_button_admin_disable_clicked)
+
+ def build_popover(self):
+ super().build_popover()
+ self.popover.opened_cb = self.cb_popover_opened
+
+ def cb_popover_opened(self, popover, user_data=None):
+ self.password_verify_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
+ password_already_set = bool(self.password)
+ self.box_admin_password.set_visible(not password_already_set)
+ self.box_admin_verify.set_visible(not password_already_set)
+ self.button_admin_disable.set_visible(password_already_set)
+
+ def passwords_match(self) -> bool:
+ password = self.password_entry.get_text()
+ # Don't accept an empty password
+ if not password:
+ return False
+ return password == self.password_verify_entry.get_text()
+
+ def apply(self):
+ # This writes the password to a file from which it will be set
+ # as the amnesia password when the greeter is closed.
+ self._root_access_setting.password = self.password
+
+ def cb_entry_admin_changed(self, editable, user_data=None):
+ self.update_check_icon()
+ passwords_match = self.passwords_match()
+ self.dialog.button_add.set_sensitive(passwords_match)
+ if passwords_match:
+ self.password = self.password_entry.get_text()
+ return False
+
+ def cb_entry_admin_activate(self, widget, user_data=None):
+ if not self.passwords_match():
+ self.dialog.button_add.set_sensitive(False)
+ self.password_verify_entry.grab_focus()
+ return False
+
+ self.password = self.password_entry.get_text()
+ self.close_window(Gtk.ResponseType.YES)
+ return False
+
+ def cb_button_admin_disable_clicked(self, widget, user_data=None):
+ self.password = None
+ self.password_entry.set_text("")
+ self.password_verify_entry.set_text("")
+
+ if self.has_popover() and self.popover.is_open():
+ self.popover.close(Gtk.ResponseType.YES)
+
+
+class MACSpoofSetting(AdditionalSetting):
+ @property
+ def id(self) -> str:
+ return "macspoof"
+
+ @property
+ def title(self) -> str:
+ return _("_MAC Address Spoofing")
+
+ @property
+ def icon_name(self) -> str:
+ return "tails-macspoof"
+
+ @property
+ def value_for_display(self) -> str:
+ return get_on_off_string(self.spoofing_enabled, default=True)
+
+ def __init__(self, physical_security_settings: "PhysicalSecuritySettings"):
+ self._physical_security_settings = physical_security_settings
+ self.spoofing_enabled = True
+ super().__init__()
+ self.accel_key = Gdk.KEY_m
+
+ self.image_macspoof_on = self.builder.get_object('image_macspoof_on')
+ self.image_macspoof_off = self.builder.get_object('image_macspoof_off')
+ self.listbox_macspoof_controls = self.builder.get_object('listbox_macspoof_controls')
+ self.listbox_macspoof_controls.connect('row-activated', self.cb_listbox_macspoof_row_activated)
+ self.listbox_macspoof_controls.connect('button-press-event', self.cb_listbox_macspoof_button_press)
+ self.listboxrow_macspoof_on = self.builder.get_object('listboxrow_macspoof_on')
+ self.listboxrow_macspoof_off = self.builder.get_object('listboxrow_macspoof_off')
+
+ def apply(self):
+ self._physical_security_settings.macspoof = self.spoofing_enabled
+
+ def cb_listbox_macspoof_row_activated(self, listbox, row, user_data=None):
+ self.spoofing_enabled = row == self.listboxrow_macspoof_on
+ self.image_macspoof_on.set_visible(self.spoofing_enabled)
+ self.image_macspoof_off.set_visible(not self.spoofing_enabled)
+
+ if self.has_popover() and self.popover.is_open():
+ self.popover.close(Gtk.ResponseType.YES)
+ return False
+
+ def cb_listbox_macspoof_button_press(self, widget, event, user_data=None):
+ # On double-click: Close the window and apply chosen setting
+ if event.type == Gdk.EventType._2BUTTON_PRESS:
+ self.close_window(Gtk.ResponseType.YES)
+ return False
+
+
+class NetworkSetting(AdditionalSetting):
+ @property
+ def id(self) -> str:
+ return "network"
+
+ @property
+ def title(self) -> str:
+ return _("_Network Connection")
+
+ @property
+ def icon_name(self) -> str:
+ return "tails-network"
+
+ @property
+ def value_for_display(self) -> str:
+ if self.netconf == self._physical_security_settings.NETCONF_DIRECT:
+ return _("Direct (default)")
+ if self.netconf == self._physical_security_settings.NETCONF_OBSTACLE:
+ return _("Bridge & Proxy")
+ if self.netconf == self._physical_security_settings.NETCONF_DISABLED:
+ return _("Offline")
+
+ def __init__(self, physical_security_settings: "PhysicalSecuritySettings"):
+ self._physical_security_settings = physical_security_settings
+ self.netconf = self._physical_security_settings.NETCONF_DIRECT
+ super().__init__()
+ self.accel_key = Gdk.KEY_n
+ self.icon_network_clear_chosen = self.builder.get_object('image_network_clear')
+ self.icon_network_specific_chosen = self.builder.get_object('image_network_specific')
+ self.icon_network_off_chosen = self.builder.get_object('image_network_off')
+ self.listbox_network_controls = self.builder.get_object('listbox_network_controls')
+ self.listbox_network_controls.connect('button-press-event', self.cb_listbox_network_button_press)
+ self.listbox_network_controls.connect('row-activated', self.cb_listbox_network_row_activated)
+ self.listboxrow_network_clear = self.builder.get_object('listboxrow_network_clear')
+ self.listboxrow_network_specific = self.builder.get_object('listboxrow_network_specific')
+ self.listboxrow_network_off = self.builder.get_object('listboxrow_network_off')
+
+ def apply(self):
+ self._physical_security_settings.netconf = self.netconf
+ is_bridge = self.netconf == self._physical_security_settings.NETCONF_OBSTACLE
+ self.main_window.set_bridge_infobar_visibility(is_bridge)
+
+ def cb_listbox_network_button_press(self, widget, event, user_data=None):
+ # On double-click: Close the window and apply chosen setting
+ if event.type == Gdk.EventType._2BUTTON_PRESS:
+ self.close_window(Gtk.ResponseType.YES)
+ return False
+
+ def cb_listbox_network_row_activated(self, listbox, row, user_data=None):
+ self.icon_network_clear_chosen.set_visible(False)
+ self.icon_network_specific_chosen.set_visible(False)
+ self.icon_network_off_chosen.set_visible(False)
+
+ if row == self.listboxrow_network_clear:
+ self.netconf = self._physical_security_settings.NETCONF_DIRECT
+ self.icon_network_clear_chosen.set_visible(True)
+ elif row == self.listboxrow_network_specific:
+ self.netconf = self._physical_security_settings.NETCONF_OBSTACLE
+ self.icon_network_specific_chosen.set_visible(True)
+ elif row == self.listboxrow_network_off:
+ self.netconf = self._physical_security_settings.NETCONF_DISABLED
+ self.icon_network_off_chosen.set_visible(True)
+
+ if self.has_popover() and self.popover.is_open():
+ self.popover.close(Gtk.ResponseType.YES)
+ return False
+
+
+def get_on_off_string(value, default=None) -> str:
+ """Return "On", "Off", "On (default)", or "Off (default)"""
+ if value and default:
+ return _("On (default)")
+ if value and not default:
+ return _("On")
+ if not value and default:
+ return _("Off")
+ if not value and not default:
+ return _("Off (default)")
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/help_window.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/help_window.py
new file mode 100644
index 0000000..51fb2f4
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/help_window.py
@@ -0,0 +1,72 @@
+import gi
+import webbrowser
+
+import tailsgreeter
+from tailsgreeter.translatable_window import TranslatableWindow
+from tailsgreeter.ui import _
+
+gi.require_version('Gtk', '3.0')
+gi.require_version('WebKit2', '4.0')
+from gi.repository import Gtk, WebKit2
+
+PREFERRED_WIDTH = 800
+
+
+class GreeterHelpWindow(Gtk.Window, TranslatableWindow):
+ """Displays a modal HTML help window"""
+
+ def __init__(self, uri):
+ Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE))
+ TranslatableWindow.__init__(self, self)
+
+ self._build_ui()
+ self.store_translations(self)
+
+ self.load_uri(uri)
+ # Replace the busy cursor set by the tails-greeter startup script with
+ # the default cursor.
+ self.get_window().set_cursor(None)
+
+ def _build_ui(self):
+ self.set_position(Gtk.WindowPosition.CENTER)
+
+ # Create HeaderBar
+ headerbar = Gtk.HeaderBar()
+ headerbar.set_show_close_button(True)
+ headerbar.show_all()
+
+ # Create webview with custom stylesheet
+ css = WebKit2.UserStyleSheet(
+ ".sidebar, .banner { display: none; }",
+ WebKit2.UserContentInjectedFrames.ALL_FRAMES,
+ WebKit2.UserStyleLevel.USER,
+ None,
+ None)
+ content_manager = WebKit2.UserContentManager()
+ content_manager.add_style_sheet(css)
+ self.webview = WebKit2.WebView.new_with_user_content_manager(
+ content_manager)
+ self.webview.connect("resource-load-started",
+ self.cb_load_started)
+ self.webview.show()
+
+ scrolledwindow = Gtk.ScrolledWindow()
+ scrolledwindow.add(self.webview)
+ scrolledwindow.show()
+
+ # Add children to ApplicationWindow
+ self.add(scrolledwindow)
+ self.set_titlebar(headerbar)
+
+ def load_uri(self, uri):
+ self.webview.load_uri(uri)
+ self.resize(
+ min(PREFERRED_WIDTH,
+ self.get_screen().get_width()),
+ self.get_screen().get_height())
+ self.present()
+
+ def cb_load_started(self, web_view, ressource, request):
+ if not request.get_uri().startswith("file://"):
+ webbrowser.open_new(request.get_uri())
+ request.set_uri(web_view.get_uri())
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py
new file mode 100644
index 0000000..ffa0950
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py
@@ -0,0 +1,401 @@
+# -*- coding: utf-8 -*-/
+#
+# Copyright 2015-2016 Tails developers <tails@boum.org>
+#
+# 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 3 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, see <http://www.gnu.org/licenses/>
+
+import locale
+import logging
+from typing import TYPE_CHECKING
+import gi
+
+import tailsgreeter # NOQA: E402
+import tailsgreeter.config # NOQA: E402
+import tailsgreeter.utils # NOQA: E402
+from tailsgreeter.translatable_window import TranslatableWindow
+from tailsgreeter.ui.popover import Popover
+from tailsgreeter.ui import _
+from tailsgreeter.ui.add_settings_dialog import AddSettingsDialog
+from tailsgreeter.ui.additional_settings import AdditionalSetting
+from tailsgreeter.ui.help_window import GreeterHelpWindow
+from tailsgreeter.ui.region_settings import RegionSetting, TextSetting
+from tailsgreeter import TRANSLATION_DOMAIN
+from tailsgreeter.ui.persistent_storage import PersistentStorage
+
+
+gi.require_version('Gdk', '3.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gdk, Gtk
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.persistence import PersistenceSettings
+ from tailsgreeter.ui.settings_collection import GreeterSettingsCollection
+
+
+MAIN_UI_FILE = 'main.ui'
+CSS_FILE = 'greeter.css'
+ICON_DIR = 'icons/'
+PREFERRED_WIDTH = 620
+PREFERRED_HEIGHT = 470
+
+locale.bindtextdomain(TRANSLATION_DOMAIN, tailsgreeter.config.locales_path)
+
+
+class GreeterMainWindow(Gtk.Window, TranslatableWindow):
+ def __init__(self, greeter, persistence_setting: "PersistenceSettings", settings: "GreeterSettingsCollection"):
+ Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE))
+ TranslatableWindow.__init__(self, self)
+ self.greeter = greeter
+ self.persistence_setting = persistence_setting
+ self.settings = settings
+
+ # Set the main_window attribute for the settings. This is required
+ # in order to allow the settings to trigger changes in the main
+ # window, for example showing an info bar.
+ for setting in self.settings:
+ setting.main_window = self
+
+ self.connect('delete-event', self.cb_window_delete_event, None)
+ self.set_position(Gtk.WindowPosition.CENTER)
+
+ # Load custom CSS
+ css_provider = Gtk.CssProvider()
+ css_provider.load_from_path(tailsgreeter.config.data_path + CSS_FILE)
+ Gtk.StyleContext.add_provider_for_screen(
+ Gdk.Screen.get_default(),
+ css_provider,
+ Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
+
+ # Load UI interface definition
+ builder = Gtk.Builder()
+ builder.set_translation_domain(TRANSLATION_DOMAIN)
+ builder.add_from_file(tailsgreeter.config.data_path + MAIN_UI_FILE)
+ builder.connect_signals(self)
+
+ for widget in builder.get_objects():
+ # Store translations for the builder objects
+ self.store_translations(widget)
+ # Workaround Gtk bug #710888 - GtkInfoBar not shown after calling
+ # gtk_widget_show:
+ # https://bugzilla.gnome.org/show_bug.cgi?id=710888
+ if isinstance(widget, Gtk.InfoBar):
+ revealer = widget.get_template_child(Gtk.InfoBar, 'revealer')
+ revealer.set_transition_type(Gtk.RevealerTransitionType.NONE)
+
+ self.box_language = builder.get_object('box_language')
+ self.box_language_header = builder.get_object('box_language_header')
+ self.box_main = builder.get_object('box_main')
+ self.box_settings = builder.get_object('box_settings')
+ self.box_settings_header = builder.get_object('box_settings_header')
+ self.box_settings_values = builder.get_object('box_settings_values')
+ self.box_storage = builder.get_object('box_storage')
+ self.box_storage_unlock = builder.get_object('box_storage_unlock')
+ self.box_storage_unlocked = builder.get_object('box_storage_unlocked')
+ self.button_storage_configure = builder.get_object('button_storage_configure')
+ self.entry_storage_passphrase = builder.get_object('entry_storage_passphrase')
+ self.frame_language = builder.get_object('frame_language')
+ self.infobar_network = builder.get_object('infobar_network')
+ self.label_settings_default = builder.get_object('label_settings_default')
+ self.listbox_add_setting = builder.get_object('listbox_add_setting')
+ self.listbox_settings = builder.get_object('listbox_settings')
+ self.toolbutton_settings_add = builder.get_object('toolbutton_settings_add')
+ self.listbox_settings = builder.get_object("listbox_settings")
+ self.listbox_region = builder.get_object("listbox_region")
+
+ # Set preferred width
+ self.set_default_size(min(Gdk.Screen.get_default().get_width(),
+ PREFERRED_WIDTH),
+ min(Gdk.Screen.get_default().get_height(),
+ PREFERRED_HEIGHT))
+
+ # Add our icon dir to icon theme
+ icon_theme = Gtk.IconTheme.get_default()
+ icon_theme.prepend_search_path(
+ tailsgreeter.config.data_path + ICON_DIR)
+
+ # Add placeholder to settings ListBox
+ self.listbox_settings.set_placeholder(self.label_settings_default)
+
+ # Persistent storage
+ self.persistent_storage = PersistentStorage(self.persistence_setting, builder)
+
+ # Add children to ApplicationWindow
+ self.add(self.box_main)
+ self.set_titlebar(self._build_headerbar())
+
+ # Set keyboard focus chain
+ self._set_focus_chain()
+
+ # Add settings to region listbox
+ for setting in self.settings.region_settings:
+ logging.debug("Adding '%s' to region listbox", setting.id)
+ self.listbox_region.add(setting.listboxrow)
+
+ # Add settings dialog
+ self.dialog_add_setting = AddSettingsDialog(builder, self.settings)
+ self.dialog_add_setting.set_transient_for(self)
+
+ # Setup keyboard accelerators
+ self._build_accelerators()
+
+ self.store_translations(self)
+
+ # Utility methods
+
+ def _build_accelerators(self):
+ accelgroup = Gtk.AccelGroup()
+ self.add_accel_group(accelgroup)
+ for accel_key in [s.accel_key for s in self.settings if s.accel_key]:
+ accelgroup.connect(
+ accel_key,
+ Gdk.ModifierType.SHIFT_MASK | Gdk.ModifierType.CONTROL_MASK,
+ Gtk.AccelFlags.VISIBLE,
+ self.cb_accelgroup_setting_activated)
+
+ def _build_headerbar(self) -> Gtk.HeaderBar:
+ headerbar = Gtk.HeaderBar()
+ headerbar_sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
+
+ self.button_shutdown = Gtk.Button.new_with_label(_("Shutdown"))
+ self.button_shutdown.connect('clicked', self.cb_button_shutdown_clicked)
+ headerbar_sizegroup.add_widget(self.button_shutdown)
+ headerbar.pack_start(self.button_shutdown)
+
+ self.button_start = Gtk.Button.new_with_mnemonic(_("_Start Tails"))
+ Gtk.StyleContext.add_class(self.button_start.get_style_context(),
+ 'suggested-action')
+ self.button_start.connect('clicked', self.cb_button_start_clicked)
+ headerbar_sizegroup.add_widget(self.button_start)
+ headerbar.pack_end(self.button_start)
+
+ headerbar.show_all()
+
+ return headerbar
+
+ def _set_focus_chain(self):
+ self.box_language.set_focus_chain([
+ self.frame_language,
+ self.box_language_header])
+ self.box_storage.set_focus_chain([
+ self.box_storage_unlock,
+ self.box_storage_unlocked,
+ self.button_storage_configure])
+ self.box_settings.set_focus_chain([
+ self.box_settings_values,
+ self.box_settings_header])
+
+ # Actions
+
+ def check_and_login(self):
+ if (self.persistence_setting.has_persistence() and
+ self.entry_storage_passphrase.get_text() and
+ not self.persistence_setting.is_unlocked):
+ logging.debug("Unlocking persistent storage before login")
+ self.persistent_storage.unlock(unlocked_cb=self.finish_login)
+ else:
+ self.finish_login()
+
+ def finish_login(self):
+ logging.info("Starting the session")
+ self.greeter.login()
+ return False
+
+ def add_setting(self, id=None):
+ response = self.dialog_add_setting.run(id)
+ if response == Gtk.ResponseType.YES:
+ row = self.listbox_add_setting.get_selected_row()
+ id = self.settings.id_from_row(row)
+ setting = self.settings.additional_settings[id]
+
+ # The setting used to be applied by the dialog itself, but there
+ # we don't know the response type in all cases. For example, we
+ # previously didn't apply the admin password in all cases if the
+ # "Add" button was clicked to close the dialog (#13447).
+ setting.apply()
+ setting.update_value_label()
+
+ self.listbox_add_setting.remove(row)
+ self.listbox_settings.add(row)
+ self.dialog_add_setting.set_visible(False)
+ self.dialog_add_setting.stack.remove(setting.box)
+ setting.build_popover()
+
+ self.listbox_settings.unselect_all()
+ if True not in [c.get_visible() for c in
+ self.listbox_add_setting.get_children()]:
+ self.toolbutton_settings_add.set_sensitive(False)
+ self.dialog_add_setting.set_visible(False)
+ else:
+ old_details = self.dialog_add_setting.stack.get_child_by_name(
+ 'setting-details')
+ if old_details:
+ self.dialog_add_setting.stack.remove(old_details)
+ self.dialog_add_setting.set_visible(False)
+
+ def edit_setting(self, id):
+ if self.settings[id].has_popover():
+ self.settings[id].listboxrow.emit("activate")
+ else:
+ self.add_setting(id)
+
+ def show(self):
+ super().show()
+ self.button_start.grab_focus()
+ self.get_root_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.ARROW))
+
+ def set_bridge_infobar_visibility(self, value: bool):
+ self.infobar_network.set_visible(value)
+
+ # Callbacks
+
+ def cb_accelgroup_setting_activated(self, accel_group, accelerable,
+ keyval, modifier):
+ for setting in self.settings:
+ if setting.accel_key == keyval:
+ self.edit_setting(setting.id)
+ return False
+
+ def cb_linkbutton_help_activate(self, linkbutton, user_data=None):
+ linkbutton.set_sensitive(False)
+ # Display progress cursor and update the UI
+ self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH))
+ while Gtk.events_pending():
+ Gtk.main_iteration()
+ # Note that we add the "file://" part here, not in the URI.
+ # We're forced to add this
+ # callback *in addition* to the standard one (Gtk.show_uri),
+ # which will do nothing for uri:s without a protocol
+ # part. This is critical since we otherwise would open the
+ # default browser (iceweasel) in T-G. If pygtk had a mechanism
+ # like gtk's g_signal_handler_find() this could be dealt with
+ # in a less messy way by just removing the default handler.
+ page = linkbutton.get_uri()
+ uri = "file:///usr/share/doc/tails/website/" + page
+ logging.debug("Opening help window for {}".format(uri))
+ helpwindow = GreeterHelpWindow(uri)
+ helpwindow.show()
+
+ def restore_linkbutton_status(widget, event, linkbutton):
+ linkbutton.set_sensitive(True)
+ return False
+
+ helpwindow.connect('delete-event', restore_linkbutton_status,
+ linkbutton)
+ # Restore default cursor
+ self.get_window().set_cursor(None)
+
+ def cb_button_shutdown_clicked(self, widget, user_data=None):
+ self.greeter.shutdown()
+ return False
+
+ def cb_button_start_clicked(self, widget, user_data=None):
+ self.check_and_login()
+ return False
+
+ def cb_button_storage_configure_clicked(self, user_data=None):
+ self.persistent_storage.configure()
+ return False
+
+ def cb_button_storage_lock_clicked(self, widget, user_data=None):
+ self.persistent_storage.lock()
+ return False
+
+ def cb_button_storage_unlock_clicked(self, widget, user_data=None):
+ self.persistent_storage.unlock()
+ return False
+
+ def cb_entry_storage_passphrase_activated(self, entry, user_data=None):
+ self.persistent_storage.unlock()
+ return False
+
+ def cb_entry_storage_passphrase_changed(self, editable, user_data=None):
+ self.persistent_storage.passphrase_changed(editable)
+ return False
+
+ def cb_infobar_close(self, infobar, user_data=None):
+ infobar.set_visible(False)
+ return False
+
+ def cb_infobar_response(self, infobar, response_id, user_data=None):
+ infobar.set_visible(False)
+ return False
+
+ def cb_listbox_add_setting_focus(self, widget, direction, user_data=None):
+ self.dialog_add_setting.listbox_focus()
+ return False
+
+ def cb_listbox_add_setting_row_activated(self, listbox, row, user_data=None):
+ self.dialog_add_setting.listbox_row_activated(row)
+ return False
+
+ def cb_listbox_region_row_activated(self, listbox, row, user_data=None):
+ setting = self.settings[self.settings.id_from_row(row)]
+ if not setting.popover.is_open():
+ setting.popover.open(self.on_region_setting_popover_closed, setting)
+ return False
+
+ def on_region_setting_popover_closed(self, popover: Popover, setting: RegionSetting):
+ # Unselect the listbox row
+ self.listbox_region.unselect_all()
+
+ if popover.response != Gtk.ResponseType.YES:
+ return
+
+ setting.apply()
+
+ # If the language is changed, the values of the other region settings
+ # are changed as well, so we have to update the value labels for all
+ # region settings in that case.
+ if isinstance(setting, TextSetting):
+ for s in self.settings.region_settings:
+ s.update_value_label()
+ else:
+ setting.update_value_label()
+
+ def cb_listbox_settings_row_activated(self, listbox, row, user_data=None):
+ setting = self.settings[self.settings.id_from_row(row)]
+ if not setting.popover.is_open():
+ setting.popover.open(self.on_additional_setting_popover_closed, setting)
+ return False
+
+ def on_additional_setting_popover_closed(self, popover: Popover, setting: AdditionalSetting):
+ logging.debug("'%s' popover closed. response: %s", setting.id, popover.response)
+ # Unselect the listbox row
+ self.listbox_settings.unselect_all()
+ if popover.response == Gtk.ResponseType.YES:
+ setting.apply()
+ setting.update_value_label()
+
+ def cb_toolbutton_settings_add_clicked(self, user_data=None):
+ self.add_setting()
+ return False
+
+ def cb_toolbutton_settings_mnemonic_activate(self, widget, group_cycling):
+ self.add_setting()
+ return False
+
+ def cb_window_delete_event(self, widget, event, user_data=None):
+ # Don't close the toplevel window on user request (e.g. pressing
+ # Alt+F4)
+ return True
+
+
+class GreeterBackgroundWindow(Gtk.ApplicationWindow):
+
+ def __init__(self, app):
+ super().__init__(app)
+ Gtk.Window.__init__(self, title=_(tailsgreeter.APPLICATION_TITLE),
+ application=app)
+ self.override_background_color(
+ Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1))
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py
new file mode 100644
index 0000000..7daddef
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py
@@ -0,0 +1,128 @@
+import logging
+import gi
+import threading
+from typing import TYPE_CHECKING
+
+from tailsgreeter.ui import _
+
+gi.require_version('GLib', '2.0')
+gi.require_version('Gtk', '3.0')
+from gi.repository import GLib, Gtk
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.persistence import PersistenceSettings
+
+
+class PersistentStorage(object):
+ def __init__(self, persistence_setting: "PersistenceSettings", builder):
+ self.persistence_setting = persistence_setting
+
+ self.box_storage = builder.get_object('box_storage')
+ self.box_storage_unlock = builder.get_object('box_storage_unlock')
+ self.box_storage_unlocked = builder.get_object('box_storage_unlocked')
+ self.button_storage_configure = builder.get_object('button_storage_configure')
+ self.button_storage_lock = builder.get_object('button_storage_lock')
+ self.button_storage_unlock = builder.get_object('button_storage_unlock')
+ self.entry_storage_passphrase = builder.get_object('entry_storage_passphrase')
+ self.image_storage_state = builder.get_object('image_storage_state')
+ self.infobar_persistence = builder.get_object('infobar_persistence')
+ self.label_infobar_persistence = builder.get_object('label_infobar_persistence')
+ self.spinner_storage_unlock = builder.get_object('spinner_storage_unlock')
+
+ if self.persistence_setting.has_persistence():
+ self.button_storage_configure.set_visible(False)
+ self.box_storage_unlock.set_visible(True)
+ self.image_storage_state.set_visible(True)
+ self.entry_storage_passphrase.set_visible(True)
+ self.spinner_storage_unlock.set_visible(False)
+ else:
+ # XXX-future: We have a nice button to configure the persistence
+ # but nothing is implemented to do so currently. So let's
+ # hide the whole thing for now.
+ self.box_storage.set_visible(False)
+
+ def configure(self):
+ # XXX-future: this should launch the configuration of the persistence.
+ logging.warning("User would be able to set up an encrypted storage.")
+ raise NotImplementedError
+
+ def lock(self):
+ if self.persistence_setting.lock():
+ self.button_storage_lock.set_visible(False)
+ self.box_storage_unlock.set_visible(True)
+ self.image_storage_state.set_visible(True)
+ self.image_storage_state.set_from_icon_name(
+ 'tails-locked', Gtk.IconSize.BUTTON)
+ self.entry_storage_passphrase.set_visible(True)
+ self.entry_storage_passphrase.set_sensitive(True)
+ self.button_storage_unlock.set_visible(True)
+ self.button_storage_unlock.set_sensitive(True)
+ self.button_storage_unlock.set_label(_("Unlock"))
+ else:
+ self.label_infobar_persistence.set_label(
+ _("Failed to relock persistent storage."))
+ self.infobar_persistence.set_visible(True)
+
+ @staticmethod
+ def passphrase_changed(editable):
+ # Remove warning icon
+ editable.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
+
+ def unlock(self, unlocked_cb=None, failed_cb=None):
+ if not unlocked_cb:
+ unlocked_cb = self.cb_unlocked
+ if not failed_cb:
+ failed_cb = self.cb_unlock_failed
+
+ self.entry_storage_passphrase.set_sensitive(False)
+ self.button_storage_unlock.set_sensitive(False)
+ self.button_storage_unlock.set_label(_("Unlocking…"))
+ self.image_storage_state.set_visible(False)
+ self.spinner_storage_unlock.set_visible(True)
+
+ passphrase = self.entry_storage_passphrase.get_text()
+
+ # Let's execute the unlocking in a thread
+ def do_unlock_storage(unlock_method, passphrase, unlocked_cb,
+ failed_cb):
+ if unlock_method(passphrase):
+ GLib.idle_add(unlocked_cb)
+ else:
+ GLib.idle_add(failed_cb)
+
+ unlocking_thread = threading.Thread(
+ target=do_unlock_storage,
+ args=(self.persistence_setting.unlock,
+ passphrase,
+ unlocked_cb,
+ failed_cb)
+
+ )
+ unlocking_thread.start()
+
+ def cb_unlock_failed(self):
+ logging.debug("Storage unlock failed")
+ self.entry_storage_passphrase.set_sensitive(True)
+ self.button_storage_unlock.set_sensitive(True)
+ self.button_storage_unlock.set_label(_("Unlock"))
+ self.image_storage_state.set_visible(True)
+ self.spinner_storage_unlock.set_visible(False)
+ self.label_infobar_persistence.set_label(
+ _("Cannot unlock encrypted storage with this passphrase."))
+ self.infobar_persistence.set_visible(True)
+ self.entry_storage_passphrase.select_region(0, -1)
+ self.entry_storage_passphrase.set_icon_from_icon_name(
+ Gtk.EntryIconPosition.SECONDARY,
+ 'dialog-warning-symbolic')
+ self.entry_storage_passphrase.grab_focus()
+
+ def cb_unlocked(self):
+ logging.debug("Storage unlocked")
+ self.spinner_storage_unlock.set_visible(False)
+ self.entry_storage_passphrase.set_visible(False)
+ self.button_storage_unlock.set_visible(False)
+ self.infobar_persistence.set_visible(False)
+ self.image_storage_state.set_from_icon_name('tails-unlocked',
+ Gtk.IconSize.BUTTON)
+ self.image_storage_state.set_visible(True)
+ self.box_storage_unlocked.set_visible(True)
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/popover.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/popover.py
new file mode 100644
index 0000000..8650ca3
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/popover.py
@@ -0,0 +1,40 @@
+import gi
+from typing import Callable, Union
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+class Popover(object):
+ def __init__(self, relative_widget, content_widget: Gtk.Popover):
+ self.widget = Gtk.Popover.new(relative_widget)
+ self.widget.set_position(Gtk.PositionType.BOTTOM)
+ self.widget.add(content_widget)
+ self.closed_cb = None # type: Union[None, Callable]
+ self.closed_cb_user_data = None
+ self.opened_cb = None # type: Union[None, Callable]
+ self.opened_cb_user_data = None
+ self.closed_signal_handler = None
+ self.response = None # type: Union[None, Gtk.ResponseType]
+
+ def on_closed(self, widget):
+ if self.closed_cb:
+ self.closed_cb(self, self.closed_cb_user_data)
+ # Make sure we only call the callback once
+ self.closed_cb = None
+ self.widget.disconnect(self.closed_signal_handler)
+
+ def open(self, closed_cb: Callable, user_data=None):
+ self.closed_cb = closed_cb
+ self.closed_cb_user_data = user_data
+ self.closed_signal_handler = self.widget.connect('closed', self.on_closed)
+ self.widget.set_visible(True)
+ if self.opened_cb:
+ self.opened_cb(self, self.opened_cb_user_data)
+
+ def close(self, response: Gtk.ResponseType):
+ self.response = response
+ self.widget.set_visible(False)
+
+ def is_open(self) -> bool:
+ return self.widget.get_visible()
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py
new file mode 100644
index 0000000..16fd73b
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py
@@ -0,0 +1,179 @@
+import logging
+import gi
+
+from tailsgreeter import TRANSLATION_DOMAIN
+import tailsgreeter.config
+from tailsgreeter.ui import _
+from tailsgreeter.ui.setting import GreeterSetting
+from tailsgreeter.ui.popover import Popover
+from typing import TYPE_CHECKING
+
+gi.require_version('Gtk', '3.0')
+gi.require_version('Pango', '1.0')
+from gi.repository import Gtk, Pango
+
+if TYPE_CHECKING:
+ from tailsgreeter.settings.localization import LocalizationSetting
+
+REGION_SETTINGS_UI_FILE = "region_settings.ui"
+
+
+class RegionSetting(GreeterSetting):
+ def __init__(self, localization_setting: "LocalizationSetting"):
+ self._localization_setting = localization_setting
+ super().__init__()
+ self.selected_code = "" # type: str
+ self.selected_name = "" # type: str
+
+ self._localization_setting.connect("notify::value", self.cb_value_changed)
+
+ self.treestore = self._localization_setting.get_tree()
+
+ self.builder = Gtk.Builder()
+ self.builder.set_translation_domain(TRANSLATION_DOMAIN)
+ self.builder.add_from_file(tailsgreeter.config.data_path + REGION_SETTINGS_UI_FILE)
+ popover_box = self.builder.get_object("box_{}_popover".format(self.id))
+ self.popover = Popover(self.listboxrow, popover_box)
+
+ self.treeview = self.builder.get_object("treeview_{}".format(self.id))
+ self.treeview.connect("row-activated", self.cb_treeview_row_activated)
+
+ # Fill the treeview
+ renderer = Gtk.CellRendererText()
+ renderer.props.ellipsize = Pango.EllipsizeMode.END
+ column = Gtk.TreeViewColumn("", renderer, text=1)
+ self.treeview.append_column(column)
+
+ searchentry = self.builder.get_object("searchentry_{}".format(self.id))
+ searchentry.connect("search-changed", self.cb_searchentry_search_changed)
+ searchentry.connect("activate", self.cb_searchentry_activate)
+
+ self.treestore_filtered = self.treestore.filter_new()
+ self.treestore_filtered.set_visible_func(self.cb_liststore_filtered_visible_func, data=searchentry)
+ self.treeview.set_model(self.treestore_filtered)
+
+ def apply(self):
+ self._localization_setting.set_value(self.selected_code)
+
+ def cb_searchentry_activate(self, searchentry, user_data=None):
+ """Selects the topmost item in the treeview when pressing Enter"""
+ if searchentry.get_text():
+ self.treeview.row_activated(Gtk.TreePath.new_from_string("0"),
+ self.treeview.get_column(0))
+ else:
+ self.popover.close(Gtk.ResponseType.CANCEL)
+
+ def cb_searchentry_search_changed(self, searchentry, user_data=None):
+ self.treestore_filtered.refilter()
+ if searchentry.get_text():
+ self.treeview.expand_all()
+ self.treeview.scroll_to_point(0, 0) # scroll to top
+ else:
+ self.treeview.collapse_all()
+ return False
+
+ def cb_treeview_row_activated(self, treeview, path, column,
+ user_data=None):
+ treemodel = treeview.get_model()
+ self.selected_code = treemodel.get_value(treemodel.get_iter(path), 0)
+ self.selected_name = treemodel.get_value(treemodel.get_iter(path), 1)
+ self.popover.close(Gtk.ResponseType.YES)
+
+ def cb_value_changed(self, obj, param):
+ logging.debug("refreshing {}".format(self._localization_setting.get_name()))
+
+ def treeview_select_line(model, path, iter, data):
+ if model.get_value(iter, 0) == data:
+ self.treeview.get_selection().select_iter(iter)
+ self.treeview.scroll_to_cell(path, use_align=True,
+ row_align=0.5)
+ return True
+ else:
+ return False
+
+ self.treestore_filtered.foreach(
+ treeview_select_line,
+ self._localization_setting.get_value())
+
+ def cb_liststore_filtered_visible_func(self, model, treeiter, searchentry):
+ search_query = searchentry.get_text().lower()
+ if not search_query:
+ return True
+
+ # Does the current node match the search?
+ value = model.get_value(treeiter, 1).lower()
+ if search_query in value:
+ return True
+
+ # Does the parent node match the search?
+ treepath = model.get_path(treeiter)
+ parent_treepath = treepath.copy()
+ parent_treepath.up()
+ if parent_treepath.get_depth() == 1:
+ # treepath is now the parent
+ parent_value = model.get_value(model.get_iter(parent_treepath), 0)
+ return search_query in parent_value
+
+ # Does any of the children nodes match the search?
+ children_treeiter = model.iter_children(treeiter)
+ while children_treeiter:
+ child_value = model.get_value(children_treeiter, 0)
+ if search_query in child_value:
+ return True
+ children_treeiter = model.iter_next(children_treeiter)
+
+ return False
+
+
+class TextSetting(RegionSetting):
+ @property
+ def id(self) -> str:
+ return "text"
+
+ @property
+ def title(self) -> str:
+ return _("_Language")
+
+ @property
+ def icon_name(self):
+ return "tails-language"
+
+ @property
+ def value_for_display(self) -> str:
+ return self._localization_setting.get_name()
+
+
+class KeyboardSetting(RegionSetting):
+ @property
+ def id(self) -> str:
+ return "keyboard"
+
+ @property
+ def title(self) -> str:
+ return _("_Keyboard Layout")
+
+ @property
+ def icon_name(self):
+ return "tails-keyboard-layout"
+
+ @property
+ def value_for_display(self) -> str:
+ return self._localization_setting.get_name()
+
+
+class FormatsSetting(RegionSetting):
+ @property
+ def id(self) -> str:
+ return "formats"
+
+ @property
+ def title(self) -> str:
+ return _("_Formats")
+
+ @property
+ def icon_name(self):
+ return "tails-formats"
+
+ @property
+ def value_for_display(self) -> str:
+ return self._localization_setting.get_name()
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/setting.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/setting.py
new file mode 100644
index 0000000..ae76384
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/setting.py
@@ -0,0 +1,56 @@
+from typing import TYPE_CHECKING
+import gi
+
+from tailsgreeter import TRANSLATION_DOMAIN
+import tailsgreeter.config
+from tailsgreeter.ui.popover import Popover, Union
+
+if TYPE_CHECKING:
+ from tailsgreeter.ui.main_window import GreeterMainWindow
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+SETTING_UI_FILE = "setting.ui"
+
+
+class GreeterSetting(object):
+ """Base class of all settings in the greeter"""
+ @property
+ def id(self) -> str:
+ return str()
+
+ @property
+ def title(self) -> str:
+ return str()
+
+ @property
+ def icon_name(self) -> str:
+ return str()
+
+ @property
+ def value_for_display(self) -> str:
+ return str()
+
+ def __init__(self):
+ self.accel_key = None
+ self.popover = None # type: Union[None, Popover]
+ self.main_window = None # type: Union[None, GreeterMainWindow]
+
+ self.builder = Gtk.Builder()
+ self.builder.set_translation_domain(TRANSLATION_DOMAIN)
+ self.builder.add_from_file((tailsgreeter.config.data_path + SETTING_UI_FILE))
+ self.listboxrow = self.builder.get_object("listboxrow") # type: Gtk.ListBoxRow
+ image = self.builder.get_object("image") # type: Gtk.Image
+ image.set_from_icon_name(self.icon_name, Gtk.IconSize.LARGE_TOOLBAR)
+ self.title_label = self.builder.get_object("label_caption")
+ self.value_label = self.builder.get_object("label_value")
+ self.title_label.set_label(self.title)
+ self.update_value_label()
+
+ def update_value_label(self):
+ self.value_label.set_label(self.value_for_display)
+
+ def has_popover(self) -> bool:
+ return self.popover is not None
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py
new file mode 100644
index 0000000..bc2619a
--- /dev/null
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/settings_collection.py
@@ -0,0 +1,36 @@
+import gi
+
+from tailsgreeter.ui.setting import GreeterSetting
+from tailsgreeter.ui.region_settings import RegionSetting
+from tailsgreeter.ui.additional_settings import AdditionalSetting
+
+gi.require_version('Gtk', '3.0')
+from gi.repository import Gtk
+
+
+class GreeterSettingsCollection(object):
+ def __init__(self, *settings: GreeterSetting, no_subclassification=False):
+ self.settings = {setting.id: setting for setting in settings}
+
+ if no_subclassification:
+ return
+
+ self.region_settings = GreeterSettingsCollection(
+ *(s for s in self.settings.values() if isinstance(s, RegionSetting)),
+ no_subclassification = True,
+ )
+ self.additional_settings = GreeterSettingsCollection(
+ *(s for s in self.settings.values() if isinstance(s, AdditionalSetting)),
+ no_subclassification = True,
+ )
+
+ def __getitem__(self, key) -> GreeterSetting:
+ return self.settings[key]
+
+ def __iter__(self):
+ return iter(self.settings.values())
+
+ def id_from_row(self, row: Gtk.ListBoxRow):
+ for setting in self.settings.values():
+ if setting.listboxrow == row:
+ return setting.id
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/utils.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/utils.py
index ffb8cce..383718a 100644
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/utils.py
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/utils.py
@@ -18,35 +18,6 @@
import logging
import subprocess
-import gi
-
-gi.require_version('Gtk', '3.0')
-from gi.repository import Gtk # NOQA: E402
-
-# Mark translatable strings, but don't actually translate them, as we
-# delegate this to TranslatableWindow that handles on-the-fly language changes
-_ = lambda text: text # NOQA: E731
-
-
-def get_on_off_string(value, default=None):
- """Return "On"|"Off" [" (default)"] based on value and default"""
- if value == default:
- if value:
- return _("On (default)")
- else:
- return _("Off (default)")
- else:
- if value:
- return _("On")
- else:
- return _("Off")
-
-
-def import_builder_objects(cls, builder, names):
- """Import Gtk.Builder objects with names as attributes of cls"""
- for name in names:
- setattr(cls, name, builder.get_object(name))
-
def check_output_and_error(args, exception, error_message, stdin=None):
"""Launch a process checking its output and raising exception if needed
@@ -72,15 +43,3 @@ def check_output_and_error(args, exception, error_message, stdin=None):
returncode=proc.returncode, stdout=out, stderr=err)
)
return out
-
-
-def setting_id_from_row(row):
- """Return a setting id from a Gtk.ListRow
-
- The Gtk.ListRow should be named listboxrow_settings_<setting_id>.
- """
- if not row:
- return None
- else:
- row_id = Gtk.Buildable.get_name(row)
- return row_id.replace("listboxrow_", "")
diff --git a/config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py b/config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py
index 513812f..aa02f92 100644
--- a/config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py
+++ b/config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py
@@ -1,12 +1,16 @@
from logging import getLogger
from typing import Union
-from gi.repository import Gtk, GLib, Gio, UDisks
-
from unlock_veracrypt_volumes import _
from unlock_veracrypt_volumes.config import TRANSLATION_DOMAIN, VOLUME_UI_FILE
from unlock_veracrypt_volumes.exceptions import UdisksObjectNotFoundError, AlreadyUnlockedError
+gi.require_version('Gtk', '3.0')
+gi.require_version('GLib', '3.0')
+gi.require_version('Gio', '2.0')
+from gi.repository import Gtk, GLib, Gio, UDisks
+
+
logger = getLogger(__name__)
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui.in b/config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui.in
new file mode 100644
index 0000000..ac54e1e
--- /dev/null
+++ b/config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui.in
@@ -0,0 +1,497 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="tails">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="box_admin_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">18</property>
+ <property name="margin_right">18</property>
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkLabel" id="label_admin_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Administration Password</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.5"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_admin_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Set up an administration password if you need to perform administrative tasks. Otherwise, the administration password is disabled for better security.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">50</property>
+ <property name="max_width_chars">50</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_admin_password">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_admin_password">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Administration Password</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_admin_password">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="placeholder_text" translatable="yes">Enter an administration password</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_admin_verify">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_admin_verify">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Confirm</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="entry_admin_verify">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="placeholder_text" translatable="yes">Confirm your administration password</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_admin_disable">
+ <property name="label" translatable="yes">Disable</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkSizeGroup" id="sizegroup_admin">
+ <widgets>
+ <widget name="label_admin_password"/>
+ <widget name="label_admin_verify"/>
+ </widgets>
+ </object>
+ <object class="GtkBox" id="box_macspoof_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">18</property>
+ <property name="margin_right">18</property>
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkLabel" id="label_macspoof_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">MAC Address Spoofing</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.5"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_macspoof_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">MAC address spoofing hides the serial number of your network interface (Wi-Fi or wired) to the local network. Spoofing MAC addresses is generally safer as it helps you hide your geographical location. But it might also create connectivity problems or look suspicious.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="width_chars">50</property>
+ <property name="max_width_chars">50</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_macspoof">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_bottom">18</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_macspoof_controls">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">browse</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_macspoof_on">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box_macspoof_on">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_macspoof_on">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Spoof all MAC addresses (default)</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">45</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_macspoof_on">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-ok-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_macspoof_off">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box_macspoof_off">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_macspoof_off">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Don't spoof MAC addresses</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">45</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_macspoof_off">
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-ok-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkBox" id="box_network_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">18</property>
+ <property name="margin_right">18</property>
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkLabel" id="label_network_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Network Configuration</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ <attribute name="scale" value="1.5"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_network_description">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">If your Internet connection is censored, filtered, or proxied you can configure a Tor bridge or a local proxy. To work completely offline, you can disable all networking.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">50</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_network_controls">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="selection_mode">browse</property>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_network_clear">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box_network_clear">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_network_clear">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Connect directly to the Tor network (default)</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">45</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_network_clear">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-ok-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_network_specific">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box_network_specific">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_network_specific">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Configure a Tor bridge or local proxy</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">45</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_network_specific">
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-ok-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child>
+ <object class="GtkListBoxRow" id="listboxrow_network_off">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box_network_off">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_network_off">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Disable all networking</property>
+ <property name="wrap">True</property>
+ <property name="max_width_chars">45</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_network_off">
+ <property name="can_focus">False</property>
+ <property name="icon_name">emblem-ok-symbolic</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+</interface>
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/greeter.ui b/config/chroot_local-includes/usr/share/tails/greeter/greeter.ui
deleted file mode 100644
index 5249aba..0000000
--- a/config/chroot_local-includes/usr/share/tails/greeter/greeter.ui
+++ /dev/null
@@ -1,1748 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- Generated with glade 3.20.0 -->
-<interface domain="tails">
- <requires lib="gtk+" version="3.20"/>
- <object class="GtkBox" id="box_admin_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">18</property>
- <child>
- <object class="GtkLabel" id="label_admin_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Administration Password</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.5"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_admin_description">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Set up an administration password if you need to perform administrative tasks. Otherwise, the administration password is disabled for better security.</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="width_chars">50</property>
- <property name="max_width_chars">50</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_admin_password">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_admin_password">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Administration Password</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="entry_admin_password">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="placeholder_text" translatable="yes">Enter an administration password</property>
- <signal name="activate" handler="cb_entry_admin_activate" swapped="no"/>
- <signal name="changed" handler="cb_entry_admin_changed" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_admin_verify">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_admin_verify">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Confirm</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkEntry" id="entry_admin_verify">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="placeholder_text" translatable="yes">Confirm your administration password</property>
- <signal name="activate" handler="cb_entry_admin_activate" swapped="no"/>
- <signal name="changed" handler="cb_entry_admin_changed" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button_admin_disable">
- <property name="label" translatable="yes">Disable</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="cb_button_admin_disable_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">4</property>
- </packing>
- </child>
- </object>
- <object class="GtkSizeGroup" id="sizegroup_admin">
- <widgets>
- <widget name="label_admin_password"/>
- <widget name="label_admin_verify"/>
- </widgets>
- </object>
- <object class="GtkBox" id="box_macspoof_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">18</property>
- <child>
- <object class="GtkLabel" id="label_macspoof_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">MAC Address Spoofing</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.5"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_macspoof_description">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">MAC address spoofing hides the serial number of your network interface (Wi-Fi or wired) to the local network. Spoofing MAC addresses is generally safer as it helps you hide your geographical location. But it might also create connectivity problems or look suspicious.</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="width_chars">50</property>
- <property name="max_width_chars">50</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame" id="frame_macspoof">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_bottom">18</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkListBox" id="listbox_macspoof_controls">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="selection_mode">browse</property>
- <signal name="row-activated" handler="cb_listbox_macspoof_row_activated" swapped="no"/>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_macspoof_on">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_macspoof_on">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_macspoof_on">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Spoof all MAC addresses (default)</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">45</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_macspoof_on">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">emblem-ok-symbolic</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_macspoof_off">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_macspoof_off">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_macspoof_off">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Don't spoof MAC addresses</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">45</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_macspoof_off">
- <property name="can_focus">False</property>
- <property name="icon_name">emblem-ok-symbolic</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label_item">
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <object class="GtkBox" id="box_main">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkInfoBar" id="infobar_persistence">
- <property name="can_focus">False</property>
- <property name="message_type">warning</property>
- <property name="show_close_button">True</property>
- <signal name="close" handler="cb_infobar_close" swapped="no"/>
- <signal name="response" handler="cb_infobar_response" swapped="no"/>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="infobar_persistence-action_area">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child internal-child="content_area">
- <object class="GtkBox" id="infobar_persistence-content_area">
- <property name="can_focus">False</property>
- <property name="spacing">16</property>
- <child>
- <object class="GtkLabel" id="label_infobar_persistence">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Cannot unlock encrypted storage with this passphrase.</property>
- <property name="wrap">True</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkInfoBar" id="infobar_network">
- <property name="app_paintable">True</property>
- <property name="can_focus">False</property>
- <property name="show_close_button">True</property>
- <signal name="close" handler="cb_infobar_close" swapped="no"/>
- <signal name="response" handler="cb_infobar_response" swapped="no"/>
- <child internal-child="action_area">
- <object class="GtkButtonBox" id="infobar_network-action_area">
- <property name="can_focus">False</property>
- <property name="spacing">6</property>
- <property name="layout_style">end</property>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child internal-child="content_area">
- <object class="GtkBox" id="infobar_network-content_area">
- <property name="can_focus">False</property>
- <property name="spacing">16</property>
- <child>
- <object class="GtkLabel" id="label_infobar_network">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">You will configure the Tor bridge and local proxy later on after connecting to a network.</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">False</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_inner">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">18</property>
- <child>
- <object class="GtkBox" id="box_header">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkLabel" id="label_header_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Welcome to Tails!</property>
- <attributes>
- <attribute name="weight" value="semibold"/>
- <attribute name="scale" value="1.5"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_language">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkBox" id="box_language_header">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_language_header_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Language &amp; Region</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLinkButton" id="linkbutton_language_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">True</property>
- <property name="relief">none</property>
- <property name="uri">doc/first_steps/startup_options.en.html#locale</property>
- <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
- <child>
- <object class="GtkImage" id="image_language_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-help</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_language_header_status">
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Default Settings</property>
- <attributes>
- <attribute name="style" value="italic"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="checkbutton_language_save">
- <property name="label" translatable="yes">Save Language &amp; Region Settings</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame" id="frame_language">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkListBox" id="listbox_language">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <signal name="row-activated" handler="cb_listbox_language_row_activated" swapped="no"/>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_text">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_text">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_text">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-language</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_text_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Language</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_text</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_text_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label">English</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_keyboard">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_keyboard">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_keyboard">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-keyboard-layout</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_keyboard_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Keyboard Layout</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_keyboard</property>
- <property name="ellipsize">end</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_keyboard_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label">English (US)</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_formats">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_formats">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_formats">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-formats</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_formats_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Formats</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_formats</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_formats_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label">United States</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label_item">
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_storage">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkBox" id="box_storage_header">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_storage_header_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Encrypted _Persistent Storage</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">entry_storage_passphrase</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_storage_state">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-locked</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkSpinner" id="spinner_storage_unlock">
- <property name="can_focus">False</property>
- <property name="active">True</property>
- <style>
- <class name="suggested-action"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkCheckButton" id="checkbutton_storage_show_passphrase">
- <property name="label" translatable="yes">Show Passphrase</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- <signal name="toggled" handler="cb_checkbutton_storage_show_passphrase_toggled" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">3</property>
- </packing>
- </child>
- <child>
- <object class="GtkLinkButton" id="linkbutton_storage_help">
- <property name="visible">False</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="relief">none</property>
- <property name="uri">doc/first_steps/persistence/use.en.html</property>
- <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
- <child>
- <object class="GtkImage" id="image_storage_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-help</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">4</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button_storage_configure">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <property name="always_show_image">True</property>
- <signal name="clicked" handler="cb_button_storage_configure_clicked" swapped="no"/>
- <child>
- <object class="GtkBox" id="box2">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <child>
- <object class="GtkImage" id="image_storage">
- <property name="can_focus">False</property>
- <property name="icon_name">tails-locked</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_storage_configure">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Configure Persistent Storage</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- </child>
- <style>
- <class name="suggested-action"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_storage_unlock">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkEntry" id="entry_storage_passphrase">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="visibility">False</property>
- <property name="placeholder_text" translatable="yes" comments="The label for this placeholder text is not very big, so keep this string short.">Enter your passphrase to unlock the persistent storage</property>
- <signal name="activate" handler="cb_entry_storage_passphrase_activated" swapped="no"/>
- <signal name="changed" handler="cb_entry_storage_passphrase_changed" swapped="no"/>
- <style>
- <class name="linked_left"/>
- </style>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkButton" id="button_storage_unlock">
- <property name="label" translatable="yes">Unlock</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="cb_button_storage_unlock_clicked" swapped="no"/>
- <style>
- <class name="suggested-action"/>
- <class name="linked_right"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- <style>
- <class name="linked"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_storage_unlocked">
- <property name="can_focus">False</property>
- <child>
- <object class="GtkButton" id="button_storage_lock">
- <property name="label" translatable="yes">Relock Persistent Storage</property>
- <property name="can_focus">True</property>
- <property name="receives_default">True</property>
- <signal name="clicked" handler="cb_button_storage_lock_clicked" swapped="no"/>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_storage_unlocked">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Your persistent storage is unlocked. Restart Tails to lock it again.</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_settings">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkBox" id="box_settings_header">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_settings_header_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Additional Settings</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">toolbutton_settings_add</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLinkButton" id="linkbutton_settings_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="receives_default">True</property>
- <property name="relief">none</property>
- <property name="uri">doc/first_steps/startup_options.en.html#additional</property>
- <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
- <child>
- <object class="GtkImage" id="image_settings_help">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-help</property>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <placeholder/>
- </child>
- <child>
- <object class="GtkCheckButton" id="checkbutton_settings_save">
- <property name="label" translatable="yes">Save Additional Settings</property>
- <property name="can_focus">True</property>
- <property name="receives_default">False</property>
- <property name="xalign">0</property>
- <property name="draw_indicator">True</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkBox" id="box_settings_values">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="orientation">vertical</property>
- <child>
- <object class="GtkFrame" id="frame_settings">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkListBox" id="listbox_settings">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <signal name="row-activated" handler="cb_listbox_settings_row_activated" after="yes" swapped="no"/>
- </object>
- </child>
- <child type="label_item">
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkToolbar" id="toolbar_settings">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="show_arrow">False</property>
- <property name="icon_size">2</property>
- <child>
- <object class="GtkToolButton" id="toolbutton_settings_add">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Add an additional setting</property>
- <property name="icon_name">list-add-symbolic</property>
- <signal name="clicked" handler="cb_toolbutton_settings_add_clicked" swapped="no"/>
- <signal name="mnemonic-activate" handler="cb_toolbutton_settings_mnemonic_activate" swapped="no"/>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="homogeneous">True</property>
- </packing>
- </child>
- <style>
- <class name="inline-toolbar"/>
- </style>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">3</property>
- </packing>
- </child>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <object class="GtkBox" id="box_network_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <property name="orientation">vertical</property>
- <property name="spacing">18</property>
- <child>
- <object class="GtkLabel" id="label_network_title">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Network Configuration</property>
- <attributes>
- <attribute name="weight" value="bold"/>
- <attribute name="scale" value="1.5"/>
- </attributes>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_network_description">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">If your Internet connection is censored, filtered, or proxied you can configure a Tor bridge or a local proxy. To work completely offline, you can disable all networking.</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">50</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkFrame" id="frame_network">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label_xalign">0</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkListBox" id="listbox_network_controls">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="selection_mode">browse</property>
- <signal name="button-press-event" handler="cb_listbox_network_button_press" swapped="no"/>
- <signal name="row-activated" handler="cb_listbox_network_row_activated" swapped="no"/>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_network_clear">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_network_clear">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_network_clear">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Connect directly to the Tor network (default)</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">45</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_network_clear">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">emblem-ok-symbolic</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_network_specific">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_network_specific">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_network_specific">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Configure a Tor bridge or local proxy</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">45</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_network_specific">
- <property name="can_focus">False</property>
- <property name="icon_name">emblem-ok-symbolic</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_network_off">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_network_off">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">12</property>
- <child>
- <object class="GtkLabel" id="label_network_off">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Disable all networking</property>
- <property name="wrap">True</property>
- <property name="max_width_chars">45</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">True</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkImage" id="image_network_off">
- <property name="can_focus">False</property>
- <property name="icon_name">emblem-ok-symbolic</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child type="label_item">
- <placeholder/>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- <object class="GtkLabel" id="label_settings_default">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="label" translatable="yes">The default settings are safe in most situations. To add a custom setting, press the "+" button below.</property>
- <property name="justify">fill</property>
- <property name="wrap">True</property>
- <property name="lines">2</property>
- <property name="xalign">0</property>
- <property name="yalign">0</property>
- </object>
- <object class="GtkListBox" id="listbox_add_setting">
- <property name="width_request">400</property>
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">18</property>
- <property name="margin_right">18</property>
- <property name="margin_top">18</property>
- <property name="margin_bottom">18</property>
- <signal name="focus" handler="cb_listbox_add_setting_focus" after="yes" swapped="no"/>
- <signal name="row-activated" handler="cb_listbox_add_setting_row_activated" swapped="no"/>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_admin">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_settings_admin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_admin">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-admin</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_admin_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Administration Password</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_admin</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_admin_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Off (default)</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_macspoof">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_macspoof">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_macspoof">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-macspoof</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_macspoof_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_MAC Address Spoofing</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_macspoof</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_macspoof_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">On (default)</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- <child>
- <object class="GtkListBoxRow" id="listboxrow_network">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <child>
- <object class="GtkBox" id="box_network">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">6</property>
- <property name="margin_right">6</property>
- <property name="margin_top">6</property>
- <property name="margin_bottom">6</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkImage" id="image_network">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="icon_name">tails-network</property>
- <property name="icon_size">3</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_network_caption">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">_Network Connection</property>
- <property name="use_underline">True</property>
- <property name="mnemonic_widget">listboxrow_network</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- <child>
- <object class="GtkLabel" id="label_network_value">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="label" translatable="yes">Direct (default)</property>
- <property name="xalign">0</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="pack_type">end</property>
- <property name="position">2</property>
- </packing>
- </child>
- </object>
- </child>
- </object>
- </child>
- </object>
- <object class="GtkListStore" id="liststore_formats">
- <columns>
- <!-- column-name id -->
- <column type="gchararray"/>
- <!-- column-name name -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkBox" id="box_formats_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">12</property>
- <property name="margin_right">12</property>
- <property name="margin_top">12</property>
- <property name="margin_bottom">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkSearchEntry" id="searchentry_formats">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="primary_icon_name">edit-find-symbolic</property>
- <property name="primary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow_formats">
- <property name="width_request">400</property>
- <property name="height_request">200</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="treeview_formats">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">liststore_formats</property>
- <property name="headers_visible">False</property>
- <property name="activate_on_single_click">True</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection_formats"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <object class="GtkListStore" id="liststore_keyboard">
- <columns>
- <!-- column-name id -->
- <column type="gchararray"/>
- <!-- column-name name -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkBox" id="box_keyboard_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">12</property>
- <property name="margin_right">12</property>
- <property name="margin_top">12</property>
- <property name="margin_bottom">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkSearchEntry" id="searchentry_keyboard">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="primary_icon_name">edit-find-symbolic</property>
- <property name="primary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow_keyboard">
- <property name="width_request">400</property>
- <property name="height_request">200</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="treeview_keyboard">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">liststore_keyboard</property>
- <property name="headers_visible">False</property>
- <property name="activate_on_single_click">True</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection_keyboard"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
- <object class="GtkListStore" id="liststore_settings_add">
- <columns>
- <!-- column-name setting-id -->
- <column type="gchararray"/>
- <!-- column-name setting-label -->
- <column type="gchararray"/>
- </columns>
- <data>
- <row>
- <col id="0">admin-account</col>
- <col id="1" translatable="yes">Administration Password</col>
- </row>
- <row>
- <col id="0">mac-spoofing</col>
- <col id="1" translatable="yes">MAC Address Spoofing</col>
- </row>
- <row>
- <col id="0">network-configuration</col>
- <col id="1" translatable="yes">Network Configuration</col>
- </row>
- </data>
- </object>
- <object class="GtkListStore" id="liststore_text">
- <columns>
- <!-- column-name id -->
- <column type="gchararray"/>
- <!-- column-name name -->
- <column type="gchararray"/>
- </columns>
- </object>
- <object class="GtkBox" id="box_text_popover">
- <property name="visible">True</property>
- <property name="can_focus">False</property>
- <property name="margin_left">12</property>
- <property name="margin_right">12</property>
- <property name="margin_top">12</property>
- <property name="margin_bottom">12</property>
- <property name="orientation">vertical</property>
- <property name="spacing">6</property>
- <child>
- <object class="GtkSearchEntry" id="searchentry_text">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="primary_icon_name">edit-find-symbolic</property>
- <property name="primary_icon_activatable">False</property>
- <property name="primary_icon_sensitive">False</property>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">0</property>
- </packing>
- </child>
- <child>
- <object class="GtkScrolledWindow" id="scrolledwindow_text">
- <property name="width_request">400</property>
- <property name="height_request">200</property>
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="hscrollbar_policy">never</property>
- <property name="shadow_type">in</property>
- <child>
- <object class="GtkTreeView" id="treeview_text">
- <property name="visible">True</property>
- <property name="can_focus">True</property>
- <property name="model">liststore_text</property>
- <property name="headers_visible">False</property>
- <property name="activate_on_single_click">True</property>
- <child internal-child="selection">
- <object class="GtkTreeSelection" id="treeview-selection_text"/>
- </child>
- </object>
- </child>
- </object>
- <packing>
- <property name="expand">False</property>
- <property name="fill">True</property>
- <property name="position">1</property>
- </packing>
- </child>
- </object>
-</interface>
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/main.ui.in b/config/chroot_local-includes/usr/share/tails/greeter/main.ui.in
new file mode 100644
index 0000000..2a058a2
--- /dev/null
+++ b/config/chroot_local-includes/usr/share/tails/greeter/main.ui.in
@@ -0,0 +1,681 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="tails">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="box_main">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkInfoBar" id="infobar_persistence">
+ <property name="can_focus">False</property>
+ <property name="message_type">warning</property>
+ <property name="show_close_button">True</property>
+ <signal name="close" handler="cb_infobar_close" swapped="no"/>
+ <signal name="response" handler="cb_infobar_response" swapped="no"/>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="infobar_persistence-action_area">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">end</property>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="content_area">
+ <object class="GtkBox" id="infobar_persistence-content_area">
+ <property name="can_focus">False</property>
+ <property name="spacing">16</property>
+ <child>
+ <object class="GtkLabel" id="label_infobar_persistence">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Cannot unlock encrypted storage with this passphrase.</property>
+ <property name="wrap">True</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkInfoBar" id="infobar_network">
+ <property name="app_paintable">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_close_button">True</property>
+ <signal name="close" handler="cb_infobar_close" swapped="no"/>
+ <signal name="response" handler="cb_infobar_response" swapped="no"/>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="infobar_network-action_area">
+ <property name="can_focus">False</property>
+ <property name="spacing">6</property>
+ <property name="layout_style">end</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child internal-child="content_area">
+ <object class="GtkBox" id="infobar_network-content_area">
+ <property name="can_focus">False</property>
+ <property name="spacing">16</property>
+ <child>
+ <object class="GtkLabel" id="label_infobar_network">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">You will configure the Tor bridge and local proxy later on after connecting to a network.</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_inner">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">18</property>
+ <property name="margin_right">18</property>
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">18</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">18</property>
+ <child>
+ <object class="GtkBox" id="box_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="label_header_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Welcome to Tails!</property>
+ <attributes>
+ <attribute name="weight" value="semibold"/>
+ <attribute name="scale" value="1.5"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_language">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="box_language_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_language_header_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Language &amp; Region</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="linkbutton_language_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="uri">doc/first_steps/startup_options.en.html#locale</property>
+ <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
+ <child>
+ <object class="GtkImage" id="image_language_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">tails-help</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_language_header_status">
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Default Settings</property>
+ <attributes>
+ <attribute name="style" value="italic"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkFrame" id="frame_language">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_region">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <signal name="row-activated" handler="cb_listbox_region_row_activated" swapped="no"/>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_storage">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="box_storage_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_storage_header_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Encrypted _Persistent Storage</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">entry_storage_passphrase</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkImage" id="image_storage_state">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">tails-locked</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinner" id="spinner_storage_unlock">
+ <property name="can_focus">False</property>
+ <property name="active">True</property>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="linkbutton_storage_help">
+ <property name="visible">False</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="uri">doc/first_steps/persistence/use.en.html</property>
+ <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
+ <child>
+ <object class="GtkImage" id="image_storage_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">tails-help</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">4</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_storage_configure">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="always_show_image">True</property>
+ <signal name="clicked" handler="cb_button_storage_configure_clicked" swapped="no"/>
+ <child>
+ <object class="GtkBox" id="box2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkImage" id="image_storage">
+ <property name="can_focus">False</property>
+ <property name="icon_name">tails-locked</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_storage_configure">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Configure Persistent Storage</property>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <style>
+ <class name="suggested-action"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_storage_unlock">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkEntry" id="entry_storage_passphrase">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="visibility">False</property>
+ <property name="placeholder_text" translatable="yes" comments="The label for this placeholder text is not very big, so keep this string short.">Enter your passphrase to unlock the persistent storage</property>
+ <signal name="activate" handler="cb_entry_storage_passphrase_activated" swapped="no"/>
+ <signal name="changed" handler="cb_entry_storage_passphrase_changed" swapped="no"/>
+ <style>
+ <class name="linked_left"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="button_storage_unlock">
+ <property name="label" translatable="yes">Unlock</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="cb_button_storage_unlock_clicked" swapped="no"/>
+ <style>
+ <class name="suggested-action"/>
+ <class name="linked_right"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ <style>
+ <class name="linked"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_storage_unlocked">
+ <property name="can_focus">False</property>
+ <child>
+ <object class="GtkButton" id="button_storage_lock">
+ <property name="label" translatable="yes">Relock Persistent Storage</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <signal name="clicked" handler="cb_button_storage_lock_clicked" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_storage_unlocked">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Your persistent storage is unlocked. Restart Tails to lock it again.</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_settings">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkBox" id="box_settings_header">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label_settings_header_title">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">_Additional Settings</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">toolbutton_settings_add</property>
+ <attributes>
+ <attribute name="weight" value="bold"/>
+ </attributes>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLinkButton" id="linkbutton_settings_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="receives_default">True</property>
+ <property name="relief">none</property>
+ <property name="uri">doc/first_steps/startup_options.en.html#additional</property>
+ <signal name="activate-link" handler="cb_linkbutton_help_activate" swapped="no"/>
+ <child>
+ <object class="GtkImage" id="image_settings_help">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="icon_name">tails-help</property>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkBox" id="box_settings_values">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkFrame" id="frame_settings">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label_xalign">0</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkListBox" id="listbox_settings">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <signal name="row-activated" handler="cb_listbox_settings_row_activated" after="yes" swapped="no"/>
+ </object>
+ </child>
+ <child type="label_item">
+ <placeholder/>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkToolbar" id="toolbar_settings">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="show_arrow">False</property>
+ <property name="icon_size">2</property>
+ <child>
+ <object class="GtkToolButton" id="toolbutton_settings_add">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes">Add an additional setting</property>
+ <property name="icon_name">list-add-symbolic</property>
+ <signal name="clicked" handler="cb_toolbutton_settings_add_clicked" swapped="no"/>
+ <signal name="mnemonic-activate" handler="cb_toolbutton_settings_mnemonic_activate" swapped="no"/>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="homogeneous">True</property>
+ </packing>
+ </child>
+ <style>
+ <class name="inline-toolbar"/>
+ </style>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">3</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkLabel" id="label_settings_default">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="label" translatable="yes">The default settings are safe in most situations. To add a custom setting, press the "+" button below.</property>
+ <property name="justify">fill</property>
+ <property name="wrap">True</property>
+ <property name="lines">2</property>
+ <property name="xalign">0</property>
+ <property name="yalign">0</property>
+ </object>
+ <object class="GtkListBox" id="listbox_add_setting">
+ <property name="width_request">400</property>
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">18</property>
+ <property name="margin_right">18</property>
+ <property name="margin_top">18</property>
+ <property name="margin_bottom">18</property>
+ <signal name="focus" handler="cb_listbox_add_setting_focus" after="yes" swapped="no"/>
+ <signal name="row-activated" handler="cb_listbox_add_setting_row_activated" swapped="no"/>
+ </object>
+ <object class="GtkListStore" id="liststore_settings_add">
+ <columns>
+ <!-- column-name setting-id -->
+ <column type="gchararray"/>
+ <!-- column-name setting-label -->
+ <column type="gchararray"/>
+ </columns>
+ <data>
+ <row>
+ <col id="0">admin-account</col>
+ <col id="1" translatable="yes">Administration Password</col>
+ </row>
+ <row>
+ <col id="0">mac-spoofing</col>
+ <col id="1" translatable="yes">MAC Address Spoofing</col>
+ </row>
+ <row>
+ <col id="0">network-configuration</col>
+ <col id="1" translatable="yes">Network Configuration</col>
+ </row>
+ </data>
+ </object>
+</interface>
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui.in b/config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui.in
new file mode 100644
index 0000000..2fb04f9
--- /dev/null
+++ b/config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui.in
@@ -0,0 +1,182 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="tails">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkBox" id="box_text_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSearchEntry" id="searchentry_text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_text">
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_text">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">liststore_text</property>
+ <property name="headers_visible">False</property>
+ <property name="activate_on_single_click">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection_text"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkBox" id="box_keyboard_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSearchEntry" id="searchentry_keyboard">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_keyboard">
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_keyboard">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">liststore_keyboard</property>
+ <property name="headers_visible">False</property>
+ <property name="activate_on_single_click">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection_keyboard"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkBox" id="box_formats_popover">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">12</property>
+ <property name="margin_right">12</property>
+ <property name="margin_top">12</property>
+ <property name="margin_bottom">12</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkSearchEntry" id="searchentry_formats">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="primary_icon_name">edit-find-symbolic</property>
+ <property name="primary_icon_activatable">False</property>
+ <property name="primary_icon_sensitive">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkScrolledWindow" id="scrolledwindow_formats">
+ <property name="width_request">400</property>
+ <property name="height_request">200</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="hscrollbar_policy">never</property>
+ <property name="shadow_type">in</property>
+ <child>
+ <object class="GtkTreeView" id="treeview_formats">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="model">liststore_formats</property>
+ <property name="headers_visible">False</property>
+ <property name="activate_on_single_click">True</property>
+ <child internal-child="selection">
+ <object class="GtkTreeSelection" id="treeview-selection_formats"/>
+ </child>
+ </object>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <object class="GtkListStore" id="liststore_text">
+ <columns>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="liststore_keyboard">
+ <columns>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+ <object class="GtkListStore" id="liststore_formats">
+ <columns>
+ <!-- column-name id -->
+ <column type="gchararray"/>
+ <!-- column-name name -->
+ <column type="gchararray"/>
+ </columns>
+ </object>
+</interface> \ No newline at end of file
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/setting.ui.in b/config/chroot_local-includes/usr/share/tails/greeter/setting.ui.in
new file mode 100644
index 0000000..38f055b
--- /dev/null
+++ b/config/chroot_local-includes/usr/share/tails/greeter/setting.ui.in
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.20.0 -->
+<interface domain="tails">
+ <requires lib="gtk+" version="3.20"/>
+ <object class="GtkListBoxRow" id="listboxrow">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <child>
+ <object class="GtkBox" id="box">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">6</property>
+ <property name="margin_right">6</property>
+ <property name="margin_top">6</property>
+ <property name="margin_bottom">6</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkImage" id="image">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_caption">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="use_underline">True</property>
+ <property name="mnemonic_widget">listboxrow</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkLabel" id="label_value">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">2</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ </object>
+</interface> \ No newline at end of file
diff --git a/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py b/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py
index 643e5ef..5819571 100755
--- a/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py
+++ b/config/chroot_local-includes/usr/share/tails/greeter/tails-greeter.py
@@ -56,16 +56,21 @@ from gi.repository import Gtk # NOQA: F401
import tailsgreeter # NOQA: F401
import tailsgreeter.config # NOQA: F401
import tailsgreeter.gdmclient # NOQA: F401
-import tailsgreeter.persistence # NOQA: F401
-import tailsgreeter.physicalsecurity # NOQA: F401
-import tailsgreeter.rootaccess # NOQA: F401
-from tailsgreeter.language import TranslatableWindow # NOQA: F401
-from tailsgreeter.gui import GreeterMainWindow # NOQA: F401
+from tailsgreeter.settings.localization_settings import LocalisationSettings
+from tailsgreeter.settings.persistence import PersistenceSettings
+from tailsgreeter.settings.physicalsecurity import PhysicalSecuritySettings
+from tailsgreeter.settings.rootaccess import RootAccessSettings
+from tailsgreeter.translatable_window import TranslatableWindow
+from tailsgreeter.ui.main_window import GreeterMainWindow # NOQA: F401
+from tailsgreeter.ui.settings_collection import GreeterSettingsCollection
+from tailsgreeter.ui.region_settings import TextSetting, KeyboardSetting, FormatsSetting
+from tailsgreeter.ui.additional_settings import AdminSetting, MACSpoofSetting, NetworkSetting
gettext.install(tailsgreeter.__appname__, tailsgreeter.config.locales_path)
locale.bindtextdomain(tailsgreeter.__appname__,
tailsgreeter.config.locales_path)
+_ = gettext.gettext
class GreeterApplication():
@@ -94,18 +99,27 @@ class GreeterApplication():
self.gdmclient = tailsgreeter.gdmclient.GdmClient(
session_opened_cb=self.close_app
)
- self.persistence = tailsgreeter.persistence.PersistenceSettings()
- self.localisationsettings = tailsgreeter.language.LocalisationSettings(
+
+ persistence = PersistenceSettings()
+ self.localisationsettings = LocalisationSettings(
usermanager_loaded_cb=self.usermanager_loaded,
locale_selected_cb=self.locale_selected
)
- self.rootaccess = \
- tailsgreeter.rootaccess.RootAccessSettings()
- self.physical_security = \
- tailsgreeter.physicalsecurity.PhysicalSecuritySettings()
+ rootaccess = RootAccessSettings()
+ physical_security = PhysicalSecuritySettings()
+
+ # Initialize the settings
+ settings = GreeterSettingsCollection(
+ TextSetting(self.localisationsettings.language),
+ KeyboardSetting(self.localisationsettings.keyboard),
+ FormatsSetting(self.localisationsettings.formats),
+ AdminSetting(rootaccess),
+ MACSpoofSetting(physical_security),
+ NetworkSetting(physical_security),
+ )
- # Load views
- self.mainwindow = GreeterMainWindow(self)
+ # Initialize main window
+ self.mainwindow = GreeterMainWindow(self, persistence, settings)
# Inhibit the session being marked as idle
self.inhibit_idle()
@@ -124,7 +138,7 @@ class GreeterApplication():
"""UserManager is ready"""
logging.debug("Entering usermanager_loaded")
self.ready = True
- self.localisationsettings.text.set_value('en_US')
+ self.localisationsettings.language.set_value('en_US')
logging.info("tails-greeter is ready.")
self.mainwindow.show()
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 650589f..6e17929 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -3,11 +3,12 @@ tmp/pot/60-tor-ready.sh.pot
tmp/pot/config.py.pot
tmp/pot/configuration-window.ui.pot
tmp/pot/electrum.pot
-tmp/pot/greeter-gui.py.pot
-tmp/pot/greeter-language.py.pot
+tmp/pot/greeter-add_settings_dialog.py.pot
+tmp/pot/greeter-additional_settings.py.pot
+tmp/pot/greeter-main_window.py.pot
tmp/pot/greeter-persistence.py.pot
-tmp/pot/greeter.ui.pot
-tmp/pot/greeter-utils.py.pot
+tmp/pot/greeter-persistent_storage.py.pot
+tmp/pot/greeter-region_settings.py.pot
tmp/pot/keepassxc.pot
tmp/pot/replace-su-with-sudo.pot
tmp/pot/status-menu-helper-extension.js.pot
@@ -43,6 +44,10 @@ config/chroot_local-includes/usr/share/desktop-directories/Tails.directory.in
# Files updated by intltool-update --xml-style
config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.root-terminal.policy.in
config/chroot_local-includes/usr/share/polkit-1/actions/org.boum.tails.additional-software.policy.in
+config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui.in
+config/chroot_local-includes/usr/share/tails/greeter/main.ui.in
+config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui.in
+config/chroot_local-includes/usr/share/tails/greeter/setting.ui.in
config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/main.ui.in
config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/volume.ui.in
config/chroot_local-includes/usr/local/share/mime/packages/unlock-veracrypt-volumes.xml.in
diff --git a/po/POTFILES.skip b/po/POTFILES.skip
index f2c7c77..18ca7c2 100644
--- a/po/POTFILES.skip
+++ b/po/POTFILES.skip
@@ -1,11 +1,18 @@
wiki/src/
config/chroot_local-includes/usr/share/doc/tails/website/
-config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/gui.py
-config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/persistence.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py
+config/chroot_local-includes/usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py
config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py
config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_list.py
config/chroot_local-includes/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_manager.py
-config/chroot_local-includes/usr/share/tails/greeter/greeter.ui
config/chroot_local-includes/usr/share/tails/additional-software/configuration-window.ui
+config/chroot_local-includes/usr/share/tails/greeter/additional_settings.ui
+config/chroot_local-includes/usr/share/tails/greeter/main.ui
+config/chroot_local-includes/usr/share/tails/greeter/region_settings.ui
+config/chroot_local-includes/usr/share/tails/greeter/setting.ui
config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/main.ui
config/chroot_local-includes/usr/share/tails/unlock-veracrypt-volumes/volume.ui
diff --git a/refresh-translations b/refresh-translations
index 083c21f..e980b62 100755
--- a/refresh-translations
+++ b/refresh-translations
@@ -14,10 +14,12 @@ PYTHON_PROGS="/etc/whisperback/config.py \
/usr/local/bin/tails-screen-locker \
/usr/local/bin/tails-upgrade-frontend-wrapper \
/usr/local/sbin/tails-additional-software \
- /usr/lib/python3/dist-packages/tailsgreeter/gui.py \
- /usr/lib/python3/dist-packages/tailsgreeter/language.py \
- /usr/lib/python3/dist-packages/tailsgreeter/persistence.py \
- /usr/lib/python3/dist-packages/tailsgreeter/utils.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/settings/persistence.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/ui/add_settings_dialog.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/ui/additional_settings.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/ui/main_window.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/ui/persistent_storage.py \
+ /usr/lib/python3/dist-packages/tailsgreeter/ui/region_settings.py \
/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume.py \
/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_list.py \
/usr/lib/python3/dist-packages/unlock_veracrypt_volumes/volume_manager.py \
@@ -33,7 +35,6 @@ JAVASCRIPT_PROGS=" \
/usr/share/gnome-shell/extensions/torstatus@tails.boum.org/extension.js \
"
GLADE_UI="\
- /usr/share/tails/greeter/greeter.ui \
/usr/share/tails/additional-software/configuration-window.ui \
"