region_settings.py 8.48 KB
Newer Older
1
2
3
4
5
import logging
import gi

from tailsgreeter import TRANSLATION_DOMAIN
import tailsgreeter.config
6
from tailsgreeter.settings import SettingNotFoundError
7
8
9
from tailsgreeter.ui import _
from tailsgreeter.ui.setting import GreeterSetting
from tailsgreeter.ui.popover import Popover
10
from typing import TYPE_CHECKING, Callable
11
12
13
14
15
16
17

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
18
19
20
21
    from tailsgreeter.settings.language import LanguageSetting
    from tailsgreeter.settings.formats import FormatsSetting
    from tailsgreeter.settings.keyboard import KeyboardSetting

22
23
24
25

REGION_SETTINGS_UI_FILE = "region_settings.ui"


segfault's avatar
segfault committed
26
class LocalizationSettingUI(GreeterSetting):
27
    def __init__(self, localization_setting: "LocalizationSetting"):
28
29
30
        self._setting = localization_setting
        self.value = self.default  # type: {str, None}
        self.value_changed_by_user = False
31
32
        super().__init__()

33
        self._setting.connect("notify::value", self.cb_value_changed)
34

35
        self.treestore = self._setting.get_tree()
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60

        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):
61
        self._setting.save(self.value, is_default=False)
segfault's avatar
segfault committed
62
        super().apply()
63

64
    def load(self) -> bool:
65
66
67
68
        try:
            value, is_default = self._setting.load()
        except SettingNotFoundError:
            raise
69
70
71
72

        if value == self.value:
            return False

73
74
75
        self.value = value
        self.value_changed_by_user = not is_default
        self.apply()
76
        return True
77
78
79
80
81
82
83
84

    @property
    def default(self) -> {str, None}:
        return None

    def on_language_changed(self, locale: str):
        pass

85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
    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()
105
106
        self.value = treemodel.get_value(treemodel.get_iter(path), 0)
        self.value_changed_by_user = True
107
108
109
        self.popover.close(Gtk.ResponseType.YES)

    def cb_value_changed(self, obj, param):
110
        logging.debug("refreshing {}".format(self._setting.get_name(self.value)))
111

segfault's avatar
segfault committed
112
113
114
        def treeview_select_line(model, path, iter_, data):
            if model.get_value(iter_, 0) == data:
                self.treeview.get_selection().select_iter(iter_)
115
116
117
118
119
120
121
122
                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,
123
                self._setting.value)
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

    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


segfault's avatar
segfault committed
155
class LanguageSettingUI(LocalizationSettingUI):
156
157
    _setting = None  # type: LanguageSetting

158
159
    @property
    def id(self) -> str:
segfault's avatar
segfault committed
160
        return "language"
161
162
163
164
165
166
167
168
169
170
171

    @property
    def title(self) -> str:
        return _("_Language")

    @property
    def icon_name(self):
        return "tails-language"

    @property
    def value_for_display(self) -> str:
172
        return self._setting.get_name(self.value)
173

174
175
176
177
178
179
180
181
182
183
184
185
186
    @property
    def default(self) -> str:
        return 'en_US'

    def __init__(self, setting: "LanguageSetting", changed_cb: Callable):
        self.changed_cb = changed_cb
        super().__init__(setting)

    def apply(self):
        super().apply()
        self._setting.apply_language(self.value)
        self.changed_cb(self.value)

187
    def load(self) -> bool:
188
        try:
189
            changed = super().load()
190
191
        except SettingNotFoundError:
            raise
192
193
194
195
        if changed:
            self._setting.apply_language(self.value)
            self.changed_cb(self.value)
        return changed
196
197
198
199


class FormatsSettingUI(LocalizationSettingUI):
    _setting = None  # type: FormatsSetting
200
201
202

    @property
    def id(self) -> str:
203
        return "formats"
204
205
206

    @property
    def title(self) -> str:
207
        return _("_Formats")
208
209
210

    @property
    def icon_name(self):
211
        return "tails-formats"
212
213
214

    @property
    def value_for_display(self) -> str:
215
        return self._setting.get_name(self.value)
216

217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    @property
    def default(self) -> str:
        return 'en_US'

    def on_language_changed(self, locale: str):
        """Set the formats according to the new language"""
        # Don't overwrite user chosen values
        if self.value_changed_by_user:
            return

        if self.value == locale:
            return

        self.value = locale
        self.update_value_label()
        self._setting.save(locale, is_default=True)


class KeyboardSettingUI(LocalizationSettingUI):
    _setting = None  # type: KeyboardSetting
237
238
239

    @property
    def id(self) -> str:
240
        return "keyboard"
241
242
243

    @property
    def title(self) -> str:
244
        return _("_Keyboard Layout")
245
246
247

    @property
    def icon_name(self):
248
        return "tails-keyboard-layout"
249
250
251

    @property
    def value_for_display(self) -> str:
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
        return self._setting.get_name(self.value)

    @property
    def default(self) -> str:
        return 'us'

    def apply(self):
        super().apply()
        self._setting.apply_layout_to_current_screen(self.value)

    def on_language_changed(self, locale: str):
        """Set the keyboard layout according to the new language"""

        # Don't overwrite a user chosen value
        if self.value_changed_by_user:
            return

        layout = self._setting.get_layout_for_locale(locale)
        if self.value == layout:
            return

        self.value = layout
        self.update_value_label()
        self._setting.save(layout, is_default=True)
276
        self._setting.apply_layout_to_current_screen(self.value)