Add todo
[tails-greeter-prototype.git] / greeter.py
blob09ecc70b826f5c0f90df1fa78e6f012979ec5788
1 #!/usr/bin/env python3
3 # Copyright 2015 Tails developers <tails@boum.org>
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation, either version 3 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>
18 ################################################################################
19 # TODO
21 # * help button should not look like buttons
22 # * settings change position when added, then removed
23 # * fill formats with formats (cf gnome-control-center)
24 # * show time when timezone is selected (we'll need to have it editable at some poit)
25 # * add a select mode for adding/removing privacy settings
26 # * add an icon representing encrypted storage
27 ################################################################################
29 import os
30 import sys
32 import gi
34 gi.require_version('GLib', '2.0')
35 from gi.repository import GLib
36 gi.require_version('Gdk', '3.0')
37 from gi.repository import Gdk
38 gi.require_version('Gtk', '3.0')
39 from gi.repository import Gtk
41 import greeterdata
43 # To ease future internationalisation
44 _ = lambda x: x
46 UI_FILE = 'greeter.ui'
47 ICON_DIR = 'icons/'
48 APPLICATION_ICON_NAME = 'gdm-setup'
49 APPLICATION_TITLE = 'Welcome to Tails!'
51 class GreeterMainWindow(Gtk.ApplicationWindow):
53 def __init__(self, app):
54 Gtk.Window.__init__(self, title=APPLICATION_TITLE, application=app)
55 self._app = app
56 self.set_icon_name(APPLICATION_ICON_NAME)
57 self._build_ui()
58 self.set_position(Gtk.WindowPosition.CENTER)
59 self._last_selected_setting_row = []
61 def _build_ui(self):
62 # Load UI interface definition
63 self._builder = Gtk.Builder()
64 self._builder.add_from_file(UI_FILE)
65 self._builder.connect_signals(self)
67 # Map interface objects
68 box_main = self._builder.get_object('box_main')
70 self.label_language_header_status = \
71 self._builder.get_object('label_language_header_status')
72 checkbutton_language_save = \
73 self._builder.get_object('checkbutton_language_save')
74 self.listbox_language = self._builder.get_object('listbox_language')
75 self.listboxrow_text = self._builder.get_object('listboxrow_text')
76 self.label_text_value = self._builder.get_object('label_text_value')
77 self.listboxrow_keyboard = self._builder.get_object('listboxrow_keyboard')
78 self.label_keyboard_value = self._builder.get_object('label_keyboard_value')
79 self.listboxrow_formats = self._builder.get_object('listboxrow_formats')
80 self.label_formats_value = self._builder.get_object('label_formats_value')
81 self.listboxrow_tz = self._builder.get_object('listboxrow_tz')
82 self.label_tz_value = self._builder.get_object('label_tz_value')
84 self.checkbutton_storage_show_passphrase = \
85 self._builder.get_object('checkbutton_storage_show_passphrase')
86 self.button_storage_configure = \
87 self._builder.get_object('button_storage_configure')
88 self.button_storage_state = self._builder.get_object('button_storage_state')
89 self.image_storage_state = self._builder.get_object('image_storage_state')
90 self.button_storage_spinner = \
91 self._builder.get_object('button_storage_spinner')
92 self.spinner_storage_unlock = \
93 self._builder.get_object('spinner_storage_unlock')
94 self.entry_storage_passphrase = \
95 self._builder.get_object('entry_storage_passphrase')
96 self.button_storage_lock = self._builder.get_object('button_storage_lock')
97 self.button_storage_unlock =self._builder.get_object('button_storage_unlock')
98 self.box_storage_unlock = self._builder.get_object('box_storage_unlock')
100 checkbutton_settings_save = \
101 self._builder.get_object('checkbutton_settings_save')
102 self.listbox_settings = self._builder.get_object('listbox_settings')
103 self.listboxrow_settings_admin = \
104 self._builder.get_object('listboxrow_settings_admin')
105 self.label_settings_admin_value = \
106 self._builder.get_object('label_settings_admin_value')
107 self.listboxrow_settings_macspoof = \
108 self._builder.get_object('listboxrow_settings_macspoof')
109 self.label_settings_macspoof_value = \
110 self._builder.get_object('label_settings_macspoof_value')
111 self.listboxrow_settings_network = \
112 self._builder.get_object('listboxrow_settings_network')
113 self.label_settings_network_value = \
114 self._builder.get_object('label_settings_network_value')
115 self.listboxrow_settings_camouflage = \
116 self._builder.get_object('listboxrow_settings_camouflage')
117 self.label_settings_camouflage_value = \
118 self._builder.get_object('label_settings_camouflage_value')
120 label_settings_default = \
121 self._builder.get_object('label_settings_default')
122 self.toolbutton_settings_remove = \
123 self._builder.get_object('toolbutton_settings_remove')
124 self.toolbutton_settings_add = \
125 self._builder.get_object('toolbutton_settings_add')
127 self.listbox_add_setting = self._builder.get_object('listbox_add_setting')
129 box_text_popover = self._builder.get_object('box_text_popover')
130 searchentry_text = self._builder.get_object('searchentry_text')
131 liststore_text = self._builder.get_object('liststore_text')
132 treeview_text = self._builder.get_object('treeview_text')
133 box_keyboard_popover = self._builder.get_object('box_keyboard_popover')
134 searchentry_keyboard = self._builder.get_object('searchentry_keyboard')
135 liststore_keyboard = self._builder.get_object('liststore_keyboard')
136 treeview_keyboard = self._builder.get_object('treeview_keyboard')
137 box_formats_popover = self._builder.get_object('box_formats_popover')
138 searchentry_formats = self._builder.get_object('searchentry_formats')
139 liststore_formats = self._builder.get_object('liststore_formats')
140 treeview_formats = self._builder.get_object('treeview_formats')
141 box_tz_popover = self._builder.get_object('box_tz_popover')
142 searchentry_tz = self._builder.get_object('searchentry_tz')
143 liststore_tz = self._builder.get_object('liststore_tz')
144 treeview_tz = self._builder.get_object('treeview_tz')
146 self.box_admin_popover = self._builder.get_object('box_admin_popover')
147 self.entry_admin_password = \
148 self._builder.get_object('entry_admin_password')
149 self.entry_admin_verify = self._builder.get_object('entry_admin_verify')
150 self.box_macspoof_popover = self._builder.get_object('box_macspoof_popover')
151 switch_macspoof = self._builder.get_object('switch_macspoof')
152 self.box_network_popover = self._builder.get_object('box_network_popover')
153 self.listbox_network_controls = \
154 self._builder.get_object('listbox_network_controls')
155 self.listboxrow_network_clear = \
156 self._builder.get_object('listboxrow_network_clear')
157 self.image_network_clear = \
158 self._builder.get_object('image_network_clear')
159 self.listboxrow_network_specific = \
160 self._builder.get_object('listboxrow_network_specific')
161 self.image_network_specific = \
162 self._builder.get_object('image_network_specific')
163 self.listboxrow_network_off = \
164 self._builder.get_object('listboxrow_network_off')
165 self.image_network_off = \
166 self._builder.get_object('image_network_off')
167 self.box_camouflage_popover = \
168 self._builder.get_object('box_camouflage_popover')
169 switch_camouflage = self._builder.get_object('switch_camouflage')
171 # Set preferred size
172 self.set_default_size(600, -1)
174 # Add our icon dir to icon theme
175 icon_theme = Gtk.IconTheme.get_default()
176 icon_theme.prepend_search_path(ICON_DIR)
178 # Add placeholder to settings ListBox
179 self.listbox_settings.set_placeholder(label_settings_default)
181 # Create HeaderBar
182 headerbar = Gtk.HeaderBar()
183 headerbar_sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
185 button_restart = Gtk.Button()
186 button_restart.set_label(_("Restart"))
187 button_restart.connect('clicked', self.cb_button_restart_clicked)
188 headerbar_sizegroup.add_widget(button_restart)
189 headerbar.pack_start(button_restart)
191 button_start = Gtk.Button()
192 button_start.set_label(_("Start Tails"))
193 Gtk.StyleContext.add_class(button_start.get_style_context(),
194 'suggested-action')
195 button_start.connect('clicked', self.cb_button_start_clicked)
196 headerbar_sizegroup.add_widget(button_start)
197 headerbar.pack_end(button_start)
199 button_tour = Gtk.Button()
200 button_tour.set_label(_("Take a Tour"))
201 button_tour.connect('clicked', self.cb_button_tour_clicked)
202 headerbar_sizegroup.add_widget(button_tour)
203 headerbar.pack_end(button_tour)
205 headerbar.show_all()
207 # Align CheckBoxes to the left
208 for cb in [checkbutton_language_save,
209 self.checkbutton_storage_show_passphrase,
210 checkbutton_settings_save]:
211 cb.set_direction(Gtk.TextDirection.RTL)
213 # Add popovers
214 self.popover_text = self.__add_popover(
215 self.listboxrow_text,
216 box_text_popover,
217 closed_cb=self.cb_popover_text_closed)
218 self.popover_keyboard = self.__add_popover(
219 self.listboxrow_keyboard,
220 box_keyboard_popover,
221 closed_cb=self.cb_popover_keyboard_closed)
222 self.popover_formats = self.__add_popover(
223 self.listboxrow_formats,
224 box_formats_popover,
225 closed_cb=self.cb_popover_formats_closed)
226 self.popover_tz = self.__add_popover(
227 self.listboxrow_tz,
228 box_tz_popover,
229 closed_cb=self.cb_popover_tz_closed)
231 # Add children to ApplicationWindow
232 self.add(box_main)
233 self.set_titlebar(headerbar)
235 # Create "Add Privacy Setting" dialog
236 self.dialog_add_setting = Gtk.Dialog(use_header_bar=True)
237 self.dialog_add_setting.set_transient_for(self)
238 self.dialog_add_setting.set_title(_("Add Privacy Setting"))
239 self.dialog_add_setting.set_default_size(-1, 400)
241 dialog_add_setting_sizegroup = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
243 self.button_add_setting_cancel = self.dialog_add_setting.add_button(
244 _("Cancel"), Gtk.ResponseType.CANCEL)
245 dialog_add_setting_sizegroup.add_widget(self.button_add_setting_cancel)
247 self.button_add_setting_add = self.dialog_add_setting.add_button(
248 _("Add"), Gtk.ResponseType.YES)
249 Gtk.StyleContext.add_class(self.button_add_setting_add.get_style_context(),
250 'suggested-action')
251 dialog_add_setting_sizegroup.add_widget(self.button_add_setting_add)
253 self.button_add_setting_add.set_visible(False)
254 self.button_add_setting_back = Gtk.Button.new_with_label(_("Back"))
255 self.button_add_setting_back.set_visible(False)
256 self.button_add_setting_back.connect(
257 'clicked', self.cb_button_add_setting_back_clicked, None)
258 dialog_add_setting_sizegroup.add_widget(self.button_add_setting_back)
259 self.dialog_add_setting.get_header_bar().pack_end(self.button_add_setting_back)
261 self.stack_add_setting = Gtk.Stack()
262 self.stack_add_setting.add_named(self.listbox_add_setting, "setting-type")
263 self.listbox_add_setting.set_valign(Gtk.Align.FILL)
264 self.listbox_add_setting.set_vexpand(True)
265 self.stack_add_setting.set_visible(True)
266 self.stack_add_setting.set_transition_type(
267 Gtk.StackTransitionType.SLIDE_LEFT_RIGHT)
268 self.dialog_add_setting.get_content_area().add(self.stack_add_setting)
270 # Fill language treeviews
271 for language in greeterdata.languages:
272 liststore_text.append(language)
273 self.__fill_tv("Text Language", treeview_text)
274 self.liststore_text_filtered = liststore_text.filter_new()
275 self.liststore_text_filtered.set_visible_func(
276 self.cb_liststore_filtered_visible_func, data=searchentry_text)
277 treeview_text.set_model(self.liststore_text_filtered)
279 for layout in greeterdata.layouts:
280 liststore_keyboard.append(layout)
281 self.__fill_tv("Keyboard Layout", treeview_keyboard)
282 self.liststore_keyboard_filtered = liststore_keyboard.filter_new()
283 self.liststore_keyboard_filtered.set_visible_func(
284 self.cb_liststore_filtered_visible_func, data=searchentry_keyboard)
285 treeview_keyboard.set_model(self.liststore_keyboard_filtered)
287 for locale in greeterdata.locales:
288 liststore_formats.append(locale)
289 self.__fill_tv("Format", treeview_formats)
290 self.liststore_formats_filtered = liststore_formats.filter_new()
291 self.liststore_formats_filtered.set_visible_func(
292 self.cb_liststore_filtered_visible_func, data=searchentry_formats)
293 treeview_formats.set_model(self.liststore_formats_filtered)
295 for timezone in greeterdata.timezones:
296 liststore_tz.append(timezone)
297 self.__fill_tv("Time Zone", treeview_tz)
298 self.liststore_tz_filtered = liststore_tz.filter_new()
299 self.liststore_tz_filtered.set_visible_func(
300 self.cb_liststore_filtered_visible_func, data=searchentry_tz)
301 treeview_tz.set_model(self.liststore_tz_filtered)
303 # Storage configuration/unlocking
304 if os.getenv('TAILS_HAS_PERSISTENCE'):
305 self.button_storage_configure.set_visible(False)
306 self.checkbutton_storage_show_passphrase.set_visible(True)
307 self.box_storage_unlock.set_visible(True)
308 self.button_storage_state.set_visible(True)
309 self.entry_storage_passphrase.set_visible(True)
310 self.button_storage_unlock.set_visible(True)
312 # Settings popovers
313 switch_macspoof.connect('notify::active', self.cb_switch_macspoof_active)
314 switch_camouflage.connect('notify::active', self.cb_switch_camouflage_active)
316 def setting_admin_check(self):
317 password = self.entry_admin_password.get_text()
318 verify = self.entry_admin_verify.get_text()
319 if verify and verify == password:
320 icon = 'emblem-ok-symbolic'
321 elif verify and not (verify == password):
322 icon = 'dialog-warning-symbolic'
323 else:
324 icon = None
325 self.entry_admin_verify.set_icon_from_icon_name(
326 Gtk.EntryIconPosition.SECONDARY, icon)
327 return (verify == password)
329 def setting_admin_apply(self):
330 if self.setting_admin_check():
331 self.label_settings_admin_value.set_text(
332 self.__get_on_off_string(self.entry_admin_password.get_text()))
333 if hasattr(self, 'popover_admin'):
334 self.popover_admin.set_visible(False)
335 return True
336 else:
337 return False
339 # Utility methods
341 @staticmethod
342 def __add_popover(widget, content, closed_cb=None):
343 popover = Gtk.Popover.new(widget)
344 popover.set_position(Gtk.PositionType.BOTTOM)
345 popover.add(content)
346 if closed_cb:
347 popover.connect('closed', closed_cb)
348 return popover
350 @staticmethod
351 def __fill_tv (name, treeview):
352 assert isinstance(name, str)
353 assert isinstance(treeview, Gtk.TreeView)
354 renderer = Gtk.CellRendererText()
355 column = Gtk.TreeViewColumn(name, renderer, text=1)
356 treeview.append_column(column)
358 @staticmethod
359 def __popover_toggle(popover):
360 if popover.get_visible():
361 popover.set_visible(False)
362 else:
363 popover.set_visible(True)
365 @staticmethod
366 def __get_on_off_string(value):
367 if value:
368 return _("On")
369 else:
370 return _("Off")
372 # Callbacks
374 def cb_button_add_setting_back_clicked(self, widget, user_data=None):
375 self.stack_add_setting.set_visible_child_name('setting-type')
376 self.stack_add_setting.remove(
377 self.stack_add_setting.get_child_by_name('setting-details'))
378 return False
380 def cb_button_help_clicked(self, widget, user_data=None):
381 dialog = Gtk.MessageDialog(
382 self,
383 Gtk.DialogFlags.DESTROY_WITH_PARENT,
384 Gtk.MessageType.INFO,
385 Gtk.ButtonsType.CLOSE,
386 _("You would see the help page for this element."))
387 dialog.run()
388 dialog.destroy()
389 return False
391 def cb_button_restart_clicked(self, widget, user_data=None):
392 self._app.quit()
393 return False
395 def cb_button_start_clicked(self, widget, user_data=None):
396 self._app.quit()
397 return False
399 def cb_button_storage_configure_clicked(self, user_data=None):
400 dialog = Gtk.MessageDialog(
401 self,
402 Gtk.DialogFlags.DESTROY_WITH_PARENT,
403 Gtk.MessageType.INFO,
404 Gtk.ButtonsType.CLOSE,
405 _("You would be able to set up an encrypted storage."))
406 dialog.run()
407 dialog.destroy()
408 return False
410 def cb_button_tour_clicked(self, user_data=None):
411 dialog = Gtk.MessageDialog(
412 self,
413 Gtk.DialogFlags.DESTROY_WITH_PARENT,
414 Gtk.MessageType.INFO,
415 Gtk.ButtonsType.CLOSE,
416 _("You would be guided through options by a wizard."))
417 dialog.run()
418 dialog.destroy()
419 return False
421 def cb_button_storage_lock_clicked(self, widget, user_data=None):
422 self.button_storage_lock.set_visible(False)
423 self.checkbutton_storage_show_passphrase.set_visible(True)
424 self.box_storage_unlock.set_visible(True)
425 self.button_storage_state.set_visible(True)
426 self.image_storage_state.set_from_icon_name('tails-locked',
427 Gtk.IconSize.BUTTON)
428 self.entry_storage_passphrase.set_visible(True)
429 self.entry_storage_passphrase.set_sensitive(True)
430 self.button_storage_unlock.set_visible(True)
431 self.button_storage_unlock.set_sensitive(True)
432 self.button_storage_unlock.set_label(_("Unlock"))
433 return False
435 def cb_button_storage_unlock_clicked(self, widget, user_data=None):
436 self.checkbutton_storage_show_passphrase.set_visible(False)
437 self.entry_storage_passphrase.set_sensitive(False)
438 self.button_storage_unlock.set_sensitive(False)
439 self.button_storage_unlock.set_label(_("Unlocking"))
440 self.button_storage_state.set_visible(False)
441 self.button_storage_spinner.set_visible(True)
442 GLib.timeout_add_seconds(2, self.cb_timeout_storage_unlocked, user_data=None)
443 return False
445 def cb_checkbutton_storage_show_passphrase_toggled(self, widget, user_data=None):
446 self.entry_storage_passphrase.set_visibility(widget.get_active())
448 def cb_entry_admin_changed(self, editable, user_data=None):
449 self.setting_admin_check()
450 return False
452 def cb_entry_admin_focus_out_event(self, widget, event, user_data=None):
453 self.setting_admin_apply()
454 return False
456 def cb_entry_admin_password_activate(self, widget, user_data=None):
457 self.entry_admin_verify.grab_focus()
458 return False
460 def cb_entry_admin_verify_activate(self, widget, user_data=None):
461 self.setting_admin_apply()
462 return False
464 def cb_listbox_add_setting_focus(self, widget, direction, user_data=None):
465 # Workaround autoselection of 1st item on focus
466 self.listbox_add_setting.unselect_all()
467 return False
469 def cb_listbox_add_setting_row_activated(self, listbox, row, user_data=None):
470 if row == self.listboxrow_settings_admin:
471 box = self.box_admin_popover
472 elif row == self.listboxrow_settings_macspoof:
473 box = self.box_macspoof_popover
474 elif row == self.listboxrow_settings_network:
475 box = self.box_network_popover
476 elif row == self.listboxrow_settings_camouflage:
477 box = self.box_camouflage_popover
478 else: # this happens when the row gets unselected
479 return False
480 self.stack_add_setting.add_named(box, 'setting-details')
481 self.stack_add_setting.set_visible_child_name('setting-details')
482 self.button_add_setting_back.set_visible(True)
483 self.button_add_setting_add.set_visible(True)
484 return False
486 def cb_listbox_language_row_activated(self, listbox, row, user_data=None):
487 if row == self.listboxrow_text:
488 self.__popover_toggle(self.popover_text)
489 elif row == self.listboxrow_keyboard:
490 self.__popover_toggle(self.popover_keyboard)
491 elif row == self.listboxrow_formats:
492 self.__popover_toggle(self.popover_formats)
493 elif row == self.listboxrow_tz:
494 self.__popover_toggle(self.popover_tz)
495 return False
497 def cb_listbox_network_row_activated(self, listbox, row, user_data=None):
498 if row == self.listboxrow_network_clear:
499 self.image_network_clear.set_visible(True)
500 self.image_network_specific.set_visible(False)
501 self.image_network_off.set_visible(False)
502 self.label_settings_network_value.set_text(_("Direct"))
503 elif row == self.listboxrow_network_specific:
504 self.image_network_specific.set_visible(True)
505 self.image_network_clear.set_visible(False)
506 self.image_network_off.set_visible(False)
507 self.label_settings_network_value.set_text(_("Censored"))
508 elif row == self.listboxrow_network_off:
509 self.image_network_off.set_visible(True)
510 self.image_network_specific.set_visible(False)
511 self.image_network_clear.set_visible(False)
512 self.label_settings_network_value.set_text(_("Off"))
513 if hasattr(self, 'popover_network'):
514 self.popover_network.set_visible(False)
515 return False
517 def cb_listbox_settings_add(self, listbox, row, user_data=None):
518 self._last_selected_setting_row.append(row)
519 if len(listbox.get_children()) > 0:
520 self.toolbutton_settings_remove.set_sensitive(True)
521 return False
523 def cb_listbox_settings_remove(self, container, object, user_data=None):
524 if len(container.get_children()) == 0:
525 self.toolbutton_settings_remove.set_sensitive(False)
526 return False
528 def cb_listbox_settings_row_selected(self, listbox, row, user_data=None):
529 if row:
530 self._last_selected_setting_row.append(row)
532 def cb_listbox_settings_row_activated(self, listbox, row, user_data=None):
533 if row == self.listboxrow_settings_admin:
534 self.__popover_toggle(self.popover_admin)
535 elif row == self.listboxrow_settings_macspoof:
536 self.__popover_toggle(self.popover_macspoof)
537 elif row == self.listboxrow_settings_network:
538 self.__popover_toggle(self.popover_network)
539 elif row == self.listboxrow_settings_camouflage:
540 self.__popover_toggle(self.popover_camouflage)
541 return False
543 def cb_liststore_filtered_visible_func(self, model, treeiter, searchentry):
544 search_query = searchentry.get_text().lower()
545 value = model.get_value(treeiter, 1).lower()
546 if not search_query:
547 return True
548 elif search_query in value:
549 return True
550 else:
551 return False
553 def cb_popover_text_closed(self, popover, user_data=None):
554 self.listbox_language.unselect_all()
555 return False
557 def cb_popover_keyboard_closed(self, popover, user_data=None):
558 self.listbox_language.unselect_all()
559 return False
561 def cb_popover_formats_closed(self, popover, user_data=None):
562 self.listbox_language.unselect_all()
563 return False
565 def cb_popover_tz_closed(self, popover, user_data=None):
566 self.listbox_language.unselect_all()
567 return False
569 def cb_popover_admin_closed(self, popover, user_data=None):
570 self.listbox_settings.unselect_all()
571 return False
573 def cb_popover_macspoof_closed(self, popover, user_data=None):
574 self.listbox_settings.unselect_all()
575 return False
577 def cb_popover_network_closed(self, popover, user_data=None):
578 self.listbox_settings.unselect_all()
579 return False
581 def cb_popover_camouflage_closed(self, popover, user_data=None):
582 self.listbox_settings.unselect_all()
583 return False
585 def cb_searchentry_formats_search_changed(self, widget, user_data=None):
586 self.liststore_formats_filtered.refilter()
587 return False
589 def cb_searchentry_keyboard_search_changed(self, widget, user_data=None):
590 self.liststore_keyboard_filtered.refilter()
591 return False
593 def cb_searchentry_text_search_changed(self, widget, user_data=None):
594 self.liststore_text_filtered.refilter()
595 return False
597 def cb_searchentry_tz_search_changed(self, widget, user_data=None):
598 self.liststore_tz_filtered.refilter()
599 return False
601 def cb_switch_camouflage_active(self, switch, pspec, user_data=None):
602 self.label_settings_camouflage_value.set_text(
603 self.__get_on_off_string(switch.get_state()))
604 if hasattr(self, 'popover_camouflage'):
605 self.popover_camouflage.set_visible(False)
607 def cb_switch_macspoof_active(self, switch, pspec, user_data=None):
608 self.label_settings_macspoof_value.set_text(
609 self.__get_on_off_string(switch.get_state()))
610 if hasattr(self, 'popover_macspoof'):
611 self.popover_macspoof.set_visible(False)
613 def cb_timeout_storage_unlocked(self, user_data=None):
614 self.button_storage_spinner.set_visible(False)
615 self.entry_storage_passphrase.set_visible(False)
616 self.button_storage_unlock.set_visible(False)
617 self.image_storage_state.set_from_icon_name('tails-unlocked',
618 Gtk.IconSize.BUTTON)
619 self.button_storage_state.set_visible(True)
620 self.button_storage_lock.set_visible(True)
621 return False
623 def cb_toolbutton_settings_add_clicked(self, user_data=None):
624 self.stack_add_setting.set_visible_child_name('setting-type')
625 self.button_add_setting_back.set_visible(False)
626 self.button_add_setting_add.set_visible(False)
627 response = self.dialog_add_setting.run()
628 if response == Gtk.ResponseType.YES:
629 row = self.listbox_add_setting.get_selected_row()
630 self.listbox_add_setting.remove(row)
631 self.listbox_settings.add(row)
632 self.dialog_add_setting.set_visible(False)
633 if row == self.listboxrow_settings_admin:
634 self.stack_add_setting.remove(self.box_admin_popover)
635 self.popover_admin = self.__add_popover(
636 self.listboxrow_settings_admin,
637 self.box_admin_popover,
638 closed_cb=self.cb_popover_admin_closed)
639 elif row == self.listboxrow_settings_macspoof:
640 self.stack_add_setting.remove(self.box_macspoof_popover)
641 self.popover_macspoof = self.__add_popover(
642 self.listboxrow_settings_macspoof,
643 self.box_macspoof_popover,
644 closed_cb=self.cb_popover_macspoof_closed)
645 elif row == self.listboxrow_settings_network:
646 self.stack_add_setting.remove(self.box_network_popover)
647 self.popover_network = self.__add_popover(
648 self.listboxrow_settings_network,
649 self.box_network_popover,
650 closed_cb=self.cb_popover_network_closed)
651 elif row == self.listboxrow_settings_camouflage:
652 self.stack_add_setting.remove(self.box_camouflage_popover)
653 self.popover_camouflage = self.__add_popover(
654 self.listboxrow_settings_camouflage,
655 self.box_camouflage_popover,
656 closed_cb=self.cb_popover_camouflage_closed)
657 self.listbox_settings.unselect_all()
658 if len(self.listbox_add_setting.get_children()) == 0:
659 self.toolbutton_settings_add.set_sensitive(False)
660 self.dialog_add_setting.set_visible(False)
661 self.dialog_add_setting.set_visible(False)
662 return False
664 def cb_toolbutton_settings_remove_clicked(self, user_data=None):
665 try:
666 row = self._last_selected_setting_row.pop()
667 except IndexError:
668 return False
669 self.listbox_settings.remove(row)
670 self.listbox_add_setting.add(row)
671 return False
673 def cb_treeview_formats_row_activated(self, treeview, path, column, user_data=None):
674 treemodel = treeview.get_model()
675 value = treemodel.get_value(treemodel.get_iter(path), 1)
676 self.label_formats_value.set_text(value)
677 self.label_language_header_status.set_text(_("Custom settings"))
678 self.popover_formats.set_visible(False)
680 def cb_treeview_keyboard_row_activated(self, treeview, path, column, user_data=None):
681 treemodel = treeview.get_model()
682 value = treemodel.get_value(treemodel.get_iter(path), 1)
683 self.label_keyboard_value.set_text(value)
684 self.label_language_header_status.set_text(_("Custom settings"))
685 self.popover_keyboard.set_visible(False)
687 def cb_treeview_text_row_activated(self, treeview, path, column, user_data=None):
688 treemodel = treeview.get_model()
689 value = treemodel.get_value(treemodel.get_iter(path), 1)
690 self.label_text_value.set_text(value)
691 self.label_language_header_status.set_text(_("Custom settings"))
692 self.popover_text.set_visible(False)
694 def cb_treeview_tz_row_activated(self, treeview, path, column, user_data=None):
695 treemodel = treeview.get_model()
696 value = treemodel.get_value(treemodel.get_iter(path), 1)
697 self.label_tz_value.set_text(value)
698 self.label_language_header_status.set_text(_("Custom settings"))
699 self.popover_tz.set_visible(False)
701 class GreeterBackgroundWindow(Gtk.ApplicationWindow):
703 def __init__(self, app):
704 Gtk.Window.__init__(self, title=APPLICATION_TITLE, application=app)
705 self.set_icon_name(APPLICATION_ICON_NAME)
706 self.override_background_color(
707 Gtk.StateFlags.NORMAL, Gdk.RGBA(0, 0, 0, 1))
709 class GreeterApplication(Gtk.Application):
711 def __init__(self):
712 Gtk.Application.__init__(self)
713 self.set_application_id = 'org.boum.tails.greeter'
715 def do_activate(self):
716 backgroundwindow = GreeterBackgroundWindow(self)
717 backgroundwindow.maximize()
718 appwindow = GreeterMainWindow(self)
719 appwindow.set_transient_for(backgroundwindow)
721 if not os.getenv('DEBUG'):
722 backgroundwindow.show()
723 appwindow.show()
725 def do_startup(self):
726 Gtk.Application.do_startup(self)
728 app = GreeterApplication()
729 exit_status = app.run(sys.argv)
730 sys.exit(exit_status)