class Network(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._jabber_sid = 0 self._radio_valid = True self._jabber_change_handler = None self._radio_change_handler = None self._wireless_configuration_reset_handler = None self._start_jabber = self._model.get_jabber() self._proxy_settings = {} self._proxy_inline_alerts = {} self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) self._radio_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.add(scrolled) scrolled.show() workspace = Gtk.VBox() scrolled.add_with_viewport(workspace) workspace.show() separator_wireless = Gtk.HSeparator() workspace.pack_start(separator_wireless, False, True, 0) separator_wireless.show() label_wireless = Gtk.Label(label=_('Wireless')) label_wireless.set_alignment(0, 0) workspace.pack_start(label_wireless, False, True, 0) label_wireless.show() box_wireless = Gtk.VBox() box_wireless.set_border_width(style.DEFAULT_SPACING * 2) box_wireless.set_spacing(style.DEFAULT_SPACING) radio_info = Gtk.Label(label=_('The wireless radio may be turned' ' off to save battery life.')) radio_info.set_alignment(0, 0) radio_info.set_line_wrap(True) radio_info.show() box_wireless.pack_start(radio_info, False, True, 0) box_radio = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._button = Gtk.CheckButton() self._button.set_alignment(0, 0) box_radio.pack_start(self._button, False, True, 0) self._button.show() label_radio = Gtk.Label(label=_('Radio')) label_radio.set_alignment(0, 0.5) box_radio.pack_start(label_radio, False, True, 0) label_radio.show() box_wireless.pack_start(box_radio, False, True, 0) box_radio.show() self._radio_alert = InlineAlert() self._radio_alert_box.pack_start(self._radio_alert, False, True, 0) box_radio.pack_end(self._radio_alert_box, False, True, 0) self._radio_alert_box.show() if 'radio' in self.restart_alerts: self._radio_alert.props.msg = self.restart_msg self._radio_alert.show() wireless_info = Gtk.Label( label=_('Discard wireless connections if' ' you have trouble connecting to the network')) wireless_info.set_alignment(0, 0) wireless_info.set_line_wrap(True) wireless_info.show() box_wireless.pack_start(wireless_info, False, True, 0) box_clear_wireless = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._clear_wireless_button = Gtk.Button() self._clear_wireless_button.set_label( _('Discard wireless connections')) box_clear_wireless.pack_start(self._clear_wireless_button, False, True, 0) if not self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(False) self._clear_wireless_button.show() box_wireless.pack_start(box_clear_wireless, False, True, 0) box_clear_wireless.show() workspace.pack_start(box_wireless, False, True, 0) box_wireless.show() separator_mesh = Gtk.HSeparator() workspace.pack_start(separator_mesh, False, False, 0) separator_mesh.show() label_mesh = Gtk.Label(label=_('Collaboration')) label_mesh.set_alignment(0, 0) workspace.pack_start(label_mesh, False, True, 0) label_mesh.show() box_mesh = Gtk.VBox() box_mesh.set_border_width(style.DEFAULT_SPACING * 2) box_mesh.set_spacing(style.DEFAULT_SPACING) server_info = Gtk.Label( _("The server is the equivalent of what" " room you are in; people on the same server" " will be able to see each other, even when" " they aren't on the same network.")) server_info.set_alignment(0, 0) server_info.set_line_wrap(True) box_mesh.pack_start(server_info, False, True, 0) server_info.show() box_server = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_server = Gtk.Label(label=_('Server:')) label_server.set_alignment(1, 0.5) label_server.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_server.pack_start(label_server, False, True, 0) group.add_widget(label_server) label_server.show() self._entry = Gtk.Entry() self._entry.set_alignment(0) self._entry.set_size_request(int(Gdk.Screen.width() / 3), -1) box_server.pack_start(self._entry, False, True, 0) self._entry.show() box_mesh.pack_start(box_server, False, True, 0) box_server.show() social_help_info = Gtk.Label( _('Social Help is a forum that lets you connect with developers' ' and discuss Sugar Activities. Changing servers means' ' discussions will happen in a different place with' ' different people.')) social_help_info.set_alignment(0, 0) social_help_info.set_line_wrap(True) box_mesh.pack_start(social_help_info, False, True, 0) social_help_info.show() social_help_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) social_help_label = Gtk.Label(label=_('Social Help Server:')) social_help_label.set_alignment(1, 0.5) social_help_label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) social_help_box.pack_start(social_help_label, False, True, 0) group.add_widget(social_help_label) social_help_label.show() self._social_help_entry = Gtk.Entry() self._social_help_entry.set_alignment(0) self._social_help_entry.set_size_request(int(Gdk.Screen.width() / 3), -1) social_help_box.pack_start(self._social_help_entry, False, True, 0) self._social_help_entry.show() box_mesh.pack_start(social_help_box, False, True, 0) social_help_box.show() workspace.pack_start(box_mesh, False, True, 0) box_mesh.show() separator_proxy = Gtk.HSeparator() workspace.pack_start(separator_proxy, False, False, 0) separator_proxy.show() self._add_proxy_section(workspace) self.setup() def _add_proxy_section(self, workspace): label_proxy = Gtk.Label(_('Proxy')) label_proxy.set_alignment(0, 0) workspace.pack_start(label_proxy, False, True, 0) label_proxy.show() box_proxy = Gtk.VBox() box_proxy.set_border_width(style.DEFAULT_SPACING * 2) box_proxy.set_spacing(style.DEFAULT_SPACING) workspace.pack_start(box_proxy, False, True, 0) box_proxy.show() self._proxy_alert = Alert() self._proxy_alert.props.title = _('Error') self._proxy_alert.props.msg = _('Proxy settings cannot be verified') box_proxy.pack_start(self._proxy_alert, False, False, 0) self._proxy_alert.connect('response', self._response_cb) self._proxy_alert.hide() # GSettings schemas for proxy: schemas = [ 'org.sugarlabs.system.proxy', 'org.sugarlabs.system.proxy.http', 'org.sugarlabs.system.proxy.https', 'org.sugarlabs.system.proxy.ftp', 'org.sugarlabs.system.proxy.socks' ] for schema in schemas: proxy_setting = Gio.Settings.new(schema) # We are not going to apply the settings immediatly. # We'll apply them if the user presses the "accept" # button, or we'll revert them if the user presses the # "cancel" button. proxy_setting.delay() alert = InlineAlert() self._proxy_settings[schema] = proxy_setting self._proxy_inline_alerts[schema] = alert size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) automatic_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) manual_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) option_sets = [('None', 'none', Gtk.VBox()), ('Use system proxy', 'system', Gtk.VBox()), ('Manual', 'manual', manual_proxy_box), ('Automatic', 'auto', automatic_proxy_box)] box_mode = ComboSettingBox( _('Method:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'mode', option_sets, size_group) box_proxy.pack_start(box_mode, False, False, 0) box_mode.show() url_box = StringSettingBox( _('Configuration URL:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'autoconfig-url', size_group) automatic_proxy_box.pack_start(url_box, True, True, 0) url_box.show() wpad_help_text = _('Web Proxy Autodiscovery is used when a' ' Configuration URL is not provided. This is not' ' recommended for untrusted public networks.') automatic_proxy_help = Gtk.Label(wpad_help_text) automatic_proxy_help.set_alignment(0, 0) automatic_proxy_help.set_line_wrap(True) automatic_proxy_help.show() automatic_proxy_box.pack_start(automatic_proxy_help, True, True, 0) # HTTP Section schema = 'org.sugarlabs.system.proxy.http' box_http = HostPortSettingBox(_('HTTP Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_http, False, False, 0) box_http.show() auth_contents_box = Gtk.VBox(spacing=style.DEFAULT_SPACING) auth_box = OptionalSettingsBox(_('Use authentication'), self._proxy_settings[schema], 'use-authentication', auth_contents_box) manual_proxy_box.pack_start(auth_box, False, False, 0) auth_box.show() proxy_http_setting = Gio.Settings.new(schema) proxy_http_setting.delay() box_username = StringSettingBox(_('Username:'******'authentication-user', size_group) auth_contents_box.pack_start(box_username, False, False, 0) box_username.show() box_password = StringSettingBox(_('Password:'******'authentication-password', size_group, password_field=True) auth_contents_box.pack_start(box_password, False, False, 0) box_password.show() # HTTPS Section schema = 'org.sugarlabs.system.proxy.https' box_https = HostPortSettingBox(_('HTTPS Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_https, False, False, 0) box_https.show() # FTP Section schema = 'org.sugarlabs.system.proxy.ftp' box_ftp = HostPortSettingBox(_('FTP Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_ftp, False, False, 0) box_ftp.show() # SOCKS Section schema = 'org.sugarlabs.system.proxy.socks' box_socks = HostPortSettingBox(_('SOCKS Proxy:'), self._proxy_inline_alerts[schema], self._proxy_settings[schema], size_group) manual_proxy_box.pack_start(box_socks, False, False, 0) box_socks.show() box_ignore = StringSettingBox_with_convert( _('Ignore Hosts:'), self._proxy_settings['org.sugarlabs.system.proxy'], 'ignore-hosts', type_as_to_string, string_to_type_as, size_group) manual_proxy_box.pack_start(box_ignore, False, False, 0) box_ignore.show() def setup(self): self._entry.set_text(self._start_jabber) self._social_help_entry.set_text(self._model.get_social_help()) try: radio_state = self._model.get_radio() except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_alert.show() else: self._button.set_active(radio_state) self._radio_valid = True self.needs_restart = False self._radio_change_handler = self._button.connect( 'toggled', self.__radio_toggled_cb) self._wireless_configuration_reset_handler = \ self._clear_wireless_button.connect( 'clicked', self.__wireless_configuration_reset_cb) def _response_cb(self, alert, response_id): if response_id is Gtk.ResponseType.APPLY: self._proxy_alert.hide() self._apply_proxy_settings() self.show_restart_alert = True self.emit('add-alert') elif response_id is Gtk.ResponseType.CANCEL: self.undo() self._proxy_alert.remove_button(Gtk.ResponseType.APPLY) self._proxy_alert.remove_button(Gtk.ResponseType.CANCEL) self._proxy_alert.hide() self.emit('set-toolbar-sensitivity', True) def _ping_servers(self): response_to_return = True non_blank_host_name_counter = 0 # To check accidental blank hostnames for schema in list(self._proxy_settings.keys()): if (schema != 'org.sugarlabs.system.proxy'): hostname = Gio.Settings.get_string( self._proxy_settings[schema], 'host') if hostname != '': non_blank_host_name_counter += 1 response = os.system("ping -c 1 -W 1 " + hostname) if (response): self._proxy_inline_alerts[schema].show() response_to_return = False if non_blank_host_name_counter == 0: response_to_return = False return response_to_return def _verify_settings(self): self._proxy_alert.props.title = _('Please Wait!') self._proxy_alert.props.msg = _('Proxy settings are being verified.') self._proxy_alert.show() flag_all_true = True g_proxy_schema = self._proxy_settings['org.sugarlabs.system.proxy'] g_mode = Gio.Settings.get_string(g_proxy_schema, 'mode') self.show_restart_alert = False if g_mode == 'auto': flag_all_true = os.path.isfile( Gio.Settings.get_string(g_proxy_schema, 'autoconfig-url')) elif g_mode == 'manual': flag_all_true = self._ping_servers() if flag_all_true: self.show_restart_alert = True self._proxy_alert.hide() self._apply_proxy_settings() else: self._proxy_alert.props.title = _('Error!') self._proxy_alert.props.msg = _('The following setting(s) seems ' 'to be incorrect and may break ' 'your internet connection') icon = Icon(icon_name='dialog-cancel') self._proxy_alert.add_button(Gtk.ResponseType.APPLY, _('Break my internet'), icon) icon.show() icon = Icon(icon_name='dialog-ok') self._proxy_alert.add_button(Gtk.ResponseType.CANCEL, _('Reset'), icon) icon.show() def _apply_proxy_settings(self): for setting in list(self._proxy_settings.values()): if (Gio.Settings.get_has_unapplied(setting)): setting.apply() def apply(self): self._apply_jabber(self._entry.get_text()) self._model.set_social_help(self._social_help_entry.get_text()) settings_changed = False for setting in list(self._proxy_settings.values()): if (Gio.Settings.get_has_unapplied(setting)): settings_changed = True if settings_changed: self.needs_restart = True self._is_cancellable = False self.restart_msg = _('Proxy changes require restart') self._verify_settings() else: self.show_restart_alert = True def undo(self): self._button.disconnect(self._radio_change_handler) self._radio_alert.hide() for setting in list(self._proxy_settings.values()): setting.revert() for alert in list(self._proxy_inline_alerts.values()): alert.hide() def _validate(self): if self._radio_valid: self.props.is_valid = True else: self.props.is_valid = False def __radio_toggled_cb(self, widget, data=None): radio_state = widget.get_active() try: self._model.set_radio(radio_state) except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_valid = False else: self._radio_valid = True if self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(True) self._validate() return False def _apply_jabber(self, jabber): if jabber == self._model.get_jabber(): return self._model.set_jabber(jabber) def __wireless_configuration_reset_cb(self, widget): # FIXME: takes effect immediately, not after CP is closed with # confirmation button self._model.clear_wireless_networks() self._clear_wireless_button.set_sensitive(False)
class AboutMe(SectionView): age_changed_signal = GObject.Signal('age-changed', arg_types=([int])) gender_changed_signal = GObject.Signal('gender-changed', arg_types=([str])) def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts if alerts else set() self.props.is_deferrable = False self._nick_sid = 0 self._color_valid = True self._nick_valid = True self._color = None self._gender = '' self._age = None self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self._color = XoColor(self._model.get_color()) self._original_nick = self._model.get_nick() self._setup_color() self._setup_nick() self._setup_gender() self._setup_age() self._update_pickers(self._color) self._nick_entry.set_text(self._original_nick) self._color_valid = True self._nick_valid = True self.needs_restart = False self._nick_entry.connect('changed', self.__nick_changed_cb) for picker in self._pickers.values(): picker.connect('color-changed', self.__color_changed_cb) self._gender_pickers.connect('gender-changed', self.__gender_changed_cb) self._age_pickers.connect('age-changed', self.__age_changed_cb) def _setup_nick(self): grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._nick_entry = Gtk.Entry() self._nick_entry.set_width_chars(25) grid.attach(self._nick_entry, 0, 0, 1, 1) self._nick_entry.show() alert_grid = Gtk.Grid() self._nick_alert = InlineAlert() alert_grid.attach(self._nick_alert, 0, 0, 1, 1) if 'nick' in self.restart_alerts: self._nick_alert.props.msg = self.restart_msg self._nick_alert.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() center_alert = Gtk.Alignment.new(0.5, 0, 0, 0) center_alert.add(alert_grid) alert_grid.show() self.pack_start(center_in_panel, False, False, 0) self.pack_start(center_alert, False, False, 0) center_in_panel.show() center_alert.show() def _setup_color(self): grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._color_alert = None self._pickers = { _PREVIOUS_FILL_COLOR: ColorPicker(_PREVIOUS_FILL_COLOR), _NEXT_FILL_COLOR: ColorPicker(_NEXT_FILL_COLOR), _CURRENT_COLOR: ColorPicker(_CURRENT_COLOR), _NEXT_STROKE_COLOR: ColorPicker(_NEXT_STROKE_COLOR), _PREVIOUS_STROKE_COLOR: ColorPicker(_PREVIOUS_STROKE_COLOR), } label_color = Gtk.Label(label=_('Click to change your color:')) label_color.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) grid.attach(label_color, 0, 0, 3, 1) label_color.show() current = 0 for picker_index in sorted(self._pickers.keys()): if picker_index == _CURRENT_COLOR: left_separator = Gtk.SeparatorToolItem() grid.attach(left_separator, current, 1, 1, 1) left_separator.show() current += 1 picker = self._pickers[picker_index] picker.show() grid.attach(picker, current, 1, 1, 1) current += 1 if picker_index == _CURRENT_COLOR: right_separator = Gtk.SeparatorToolItem() right_separator.show() grid.attach(right_separator, current, 1, 1, 1) current += 1 label_color_error = Gtk.Label() grid.attach(label_color_error, 0, 2, 3, 1) label_color_error.show() self._color_alert = InlineAlert() grid.attach(self._color_alert, 0, 3, 3, 1) if 'color' in self.restart_alerts: self._color_alert.props.msg = self.restart_msg self._color_alert.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def _setup_gender(self): self._saved_gender = load_gender() self._gender_pickers = GenderPicker() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) label_gender = Gtk.Label(label=_('Select gender:')) label_gender.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) grid.attach(label_gender, 0, 0, 1, 1) label_gender.show() grid.attach(self._gender_pickers, 0, 1, 1, 1) self._gender_pickers.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def _setup_age(self): self._saved_age = load_age() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._age_pickers = AgePicker(self._saved_gender) center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(self._age_pickers) self._age_pickers.show() label = self._age_pickers.get_label() label_age = Gtk.Label(label=_(label)) label_age.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) left_align = Gtk.Alignment.new(0, 0, 0, 0) left_align.add(label_age) label_age.show() grid.attach(left_align, 0, 0, 1, 1) left_align.show() grid.attach(center_in_panel, 0, 1, 1, 1) center_in_panel.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def setup(self): pass def undo(self): self._model.undo() self._nick_alert.hide() self._color_alert.hide() # Undo gender or age changes save_gender(self._saved_gender) save_age(self._saved_age) def _update_pickers(self, color): for picker in self._pickers.values(): picker.props.color = color self._gender_pickers.update_color(color) self._age_pickers.update_color(color) def _validate(self): if self._nick_valid and self._color_valid: self.props.is_valid = True else: self.props.is_valid = False def __nick_changed_cb(self, widget, data=None): if self._nick_sid: GLib.source_remove(self._nick_sid) self._nick_sid = GLib.timeout_add(self._APPLY_TIMEOUT, self.__nick_timeout_cb, widget) def __nick_timeout_cb(self, widget): self._nick_sid = 0 if widget.get_text() == self._model.get_nick(): self.restart_alerts.remove('nick') if not self.restart_alerts: self.needs_restart = False self._nick_alert.hide() return False try: self._model.set_nick(widget.get_text()) except ValueError, detail: self._nick_alert.props.msg = detail self._nick_valid = False self._nick_alert.show() else:
class Frame(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self._corner_delay_sid = 0 self._corner_delay_is_valid = True self._corner_delay_change_handler = None self._edge_delay_sid = 0 self._edge_delay_is_valid = True self._edge_delay_change_handler = None self._trigger_size_sid = 0 self._trigger_size_is_valid = True self._trigger_size_change_handler = None self.restart_alerts = alerts self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self._group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) separator = Gtk.HSeparator() self.pack_start(separator, False, True, 0) separator.show() label_activation = Gtk.Label(label=_('Activation Delay')) label_activation.set_alignment(0, 0) self.pack_start(label_activation, False, True, 0) label_activation.show() self._box_sliders = Gtk.VBox() self._box_sliders.set_border_width(style.DEFAULT_SPACING * 2) self._box_sliders.set_spacing(style.DEFAULT_SPACING) self._corner_delay_slider = None self._corner_delay_alert = None self._setup_corner() self._edge_delay_slider = None self._edge_delay_alert = None self._setup_edge() self._trigger_size_slider = None self._trigger_size_alert = None self._setup_trigger() self.pack_start(self._box_sliders, False, True, 0) self._box_sliders.show() self.setup() def _setup_corner(self): box_delay = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_delay = Gtk.Label(label=_('Corner')) label_delay.set_alignment(1, 0.75) label_delay.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_delay.pack_start(label_delay, False, True, 0) self._group.add_widget(label_delay) label_delay.show() adj = Gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, step_incr=100, page_incr=100, page_size=0) self._corner_delay_slider = Gtk.HScale() self._corner_delay_slider.set_adjustment(adj) self._corner_delay_slider.set_digits(0) self._corner_delay_slider.connect('format-value', self.__corner_delay_format_cb) box_delay.pack_start(self._corner_delay_slider, True, True, 0) self._corner_delay_slider.show() self._box_sliders.pack_start(box_delay, False, True, 0) box_delay.show() self._corner_delay_alert = InlineAlert() label_delay_error = Gtk.Label() self._group.add_widget(label_delay_error) delay_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) delay_alert_box.pack_start(label_delay_error, False, True, 0) label_delay_error.show() delay_alert_box.pack_start(self._corner_delay_alert, False, True, 0) self._box_sliders.pack_start(delay_alert_box, False, True, 0) delay_alert_box.show() if 'corner_delay' in self.restart_alerts: self._corner_delay_alert.props.msg = self.restart_msg self._corner_delay_alert.show() def _setup_edge(self): box_delay = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_delay = Gtk.Label(label=_('Edge')) label_delay.set_alignment(1, 0.75) label_delay.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_delay.pack_start(label_delay, False, True, 0) self._group.add_widget(label_delay) label_delay.show() adj = Gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, step_incr=100, page_incr=100, page_size=0) self._edge_delay_slider = Gtk.HScale() self._edge_delay_slider.set_adjustment(adj) self._edge_delay_slider.set_digits(0) self._edge_delay_slider.connect('format-value', self.__edge_delay_format_cb) box_delay.pack_start(self._edge_delay_slider, True, True, 0) self._edge_delay_slider.show() self._box_sliders.pack_start(box_delay, False, True, 0) box_delay.show() self._edge_delay_alert = InlineAlert() label_delay_error = Gtk.Label() self._group.add_widget(label_delay_error) delay_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) delay_alert_box.pack_start(label_delay_error, False, True, 0) label_delay_error.show() delay_alert_box.pack_start(self._edge_delay_alert, False, True, 0) self._box_sliders.pack_start(delay_alert_box, False, True, 0) delay_alert_box.show() if 'edge_delay' in self.restart_alerts: self._edge_delay_alert.props.msg = self.restart_msg self._edge_delay_alert.show() def _setup_trigger(self): box_trigger = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_trigger = Gtk.Label(label=_('Trigger Size')) label_trigger.set_alignment(1, 0.75) label_trigger.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_trigger.pack_start(label_trigger, False, True, 0) self._group.add_widget(label_trigger) label_trigger.show() adj = Gtk.Adjustment(value=1, lower=1, upper=style.GRID_CELL_SIZE, step_incr=1, page_incr=1, page_size=0) self._trigger_size_slider = Gtk.HScale() self._trigger_size_slider.set_adjustment(adj) self._trigger_size_slider.set_digits(0) self._trigger_size_slider.connect('format-value', self.__trigger_size_format_cb) box_trigger.pack_start(self._trigger_size_slider, True, True, 0) self._trigger_size_slider.show() self._box_sliders.pack_start(box_trigger, False, True, 0) box_trigger.show() self._trigger_size_alert = InlineAlert() label_trigger_error = Gtk.Label() self._group.add_widget(label_trigger_error) trigger_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) trigger_alert_box.pack_start(label_trigger_error, False, True, 0) label_trigger_error.show() trigger_alert_box.pack_start(self._trigger_size_alert, False, True, 0) self._box_sliders.pack_start(trigger_alert_box, False, True, 0) trigger_alert_box.show() if 'trigger_size' in self.restart_alerts: self._trigger_size_alert.props.msg = self.restart_msg self._trigger_size_alert.show() def setup(self): self._corner_delay_slider.set_value(self._model.get_corner_delay()) self._edge_delay_slider.set_value(self._model.get_edge_delay()) self._trigger_size_slider.set_value(self._model.get_trigger_size()) self._corner_delay_is_valid = True self._edge_delay_is_valid = True self._trigger_size_is_valid = True self.needs_restart = False self._corner_delay_change_handler = self._corner_delay_slider.connect( 'value-changed', self.__corner_delay_changed_cb) self._edge_delay_change_handler = self._edge_delay_slider.connect( 'value-changed', self.__edge_delay_changed_cb) self._trigger_size_change_handler = self._trigger_size_slider.connect( 'value-changed', self.__trigger_size_changed_cb) def undo(self): self._corner_delay_slider.disconnect(self._corner_delay_change_handler) self._edge_delay_slider.disconnect(self._edge_delay_change_handler) self._trigger_size_slider.disconnect(self._trigger_size_change_handler) self._model.undo() self._corner_delay_alert.hide() self._edge_delay_alert.hide() self._trigger_size_alert.hide() def _validate(self): if self._edge_delay_is_valid and self._corner_delay_is_valid \ and self._trigger_size_is_valid: self.props.is_valid = True else: self.props.is_valid = False def __corner_delay_changed_cb(self, scale, data=None): if self._corner_delay_sid: GObject.source_remove(self._corner_delay_sid) self._corner_delay_sid = GObject.timeout_add( self._APPLY_TIMEOUT, self.__corner_delay_timeout_cb, scale) def __corner_delay_timeout_cb(self, scale): self._corner_delay_sid = 0 if scale.get_value() == self._model.get_corner_delay(): return False try: self._model.set_corner_delay(scale.get_value()) except ValueError, detail: self._corner_delay_alert.props.msg = detail self._corner_delay_is_valid = False else:
class Language(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._lang_sid = 0 self._selected_lang_count = 0 self._labels = [] self._stores = [] self._comboboxes = [] self._add_remove_boxes = [] self._changed = False self._cursor_change_handler = None self._available_locales = self._model.read_all_languages() self._selected_locales = self._model.get_languages() self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) explanation = gettext.gettext('Add languages in the order you prefer.' ' If a translation is not available,' ' the next in the list will be used.') self._text = Gtk.Label(label=explanation) self._text.set_line_wrap(True) self._text.set_alignment(0, 0) self.pack_start(self._text, False, False, 0) self._text.show() scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled.show() self.pack_start(scrolled, True, True, 0) self._table = Gtk.Table(rows=1, columns=3, homogeneous=False) self._table.set_border_width(style.DEFAULT_SPACING * 2) self._table.show() scrolled.add_with_viewport(self._table) self._lang_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._lang_alert_box, False, True, 0) self._lang_alert = InlineAlert() self._lang_alert_box.pack_start(self._lang_alert, True, True, 0) if 'lang' in self.restart_alerts: self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() self._lang_alert_box.show() self.setup() def _add_row(self, locale_code=None): """Adds a row to the table""" self._selected_lang_count += 1 self._table.resize(self._selected_lang_count, 3) label = Gtk.Label(label=str(self._selected_lang_count)) label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._labels.append(label) self._attach_to_table(label, 0, 1, padding=1) label.show() store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING) for language, country, code in self._available_locales: description = '%s (%s)' % (_translate_language(language), _translate_country(country)) store.append([code, description]) combobox = Gtk.ComboBox(model=store) cell = Gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) if locale_code: for row in store: lang = locale_code.split('.')[0] lang_column = row[0].split('.')[0] if lang in lang_column: combobox.set_active_iter(row.iter) break else: combobox.set_active(1) combobox.connect('changed', self.__combobox_changed_cb) self._stores.append(store) self._comboboxes.append(combobox) self._attach_to_table(combobox, 1, 2, yoptions=Gtk.AttachOptions.SHRINK) add_remove_box = self._create_add_remove_box() self._add_remove_boxes.append(add_remove_box) self._attach_to_table(add_remove_box, 2, 3) add_remove_box.show_all() if self._selected_lang_count > 1: previous_add_removes = self._add_remove_boxes[-2] previous_add_removes.hide() # Hide the Remove button if the new added row is the only # language. elif self._selected_lang_count == 1: add_button_, remove_button = add_remove_box.get_children() remove_button.props.visible = False combobox.show() def _attach_to_table(self, widget, row, column, padding=20, yoptions=Gtk.AttachOptions.FILL): self._table.attach(widget, row, column, self._selected_lang_count - 1, self._selected_lang_count, xoptions=Gtk.AttachOptions.FILL, yoptions=yoptions, xpadding=padding, ypadding=padding) def _delete_last_row(self): """Deletes the last row of the table""" self._selected_lang_count -= 1 label, add_remove_box, combobox, store_ = self._get_last_row() label.destroy() add_remove_box.destroy() combobox.destroy() self._table.resize(self._selected_lang_count, 3) self._add_remove_boxes[-1].show_all() # Hide or show the Remove button in the new last row, # depending if it is the only language. add_remove_box = self._add_remove_boxes[-1] add_button_, remove_button = add_remove_box.get_children() if self._selected_lang_count == 1: remove_button.props.visible = False else: remove_button.props.visible = True def _get_last_row(self): label = self._labels.pop() add_remove_box = self._add_remove_boxes.pop() combobox = self._comboboxes.pop() store = self._stores.pop() return label, add_remove_box, combobox, store def setup(self): for locale in self._selected_locales: self._add_row(locale_code=locale) def undo(self): self._model.undo() self._lang_alert.hide() def _create_add_remove_box(self): """Creates Gtk.Hbox with add/remove buttons""" add_icon = Icon(icon_name='list-add') add_button = Gtk.Button() add_button.set_image(add_icon) add_button.connect('clicked', self.__add_button_clicked_cb) remove_icon = Icon(icon_name='list-remove') remove_button = Gtk.Button() remove_button.set_image(remove_icon) remove_button.connect('clicked', self.__remove_button_clicked_cb) add_remove_box = Gtk.HButtonBox() add_remove_box.set_layout(Gtk.ButtonBoxStyle.START) add_remove_box.set_spacing(10) add_remove_box.pack_start(add_button, True, True, 0) add_remove_box.pack_start(remove_button, True, True, 0) return add_remove_box def __add_button_clicked_cb(self, button): self._add_row() self._check_change() def __remove_button_clicked_cb(self, button): self._delete_last_row() self._check_change() def __combobox_changed_cb(self, button): self._check_change() def _check_change(self): selected_langs = self._get_selected_langs() self._changed = (selected_langs != self._selected_locales) if self._changed is False: # The user reverted back to the original config self.needs_restart = False if 'lang' in self.restart_alerts: self.restart_alerts.remove('lang') self._lang_alert.hide() if self._lang_sid: GObject.source_remove(self._lang_sid) self._model.undo() return False if self._lang_sid: GObject.source_remove(self._lang_sid) self._lang_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__lang_timeout_cb, selected_langs) def _get_selected_langs(self): new_codes = [] for combobox in self._comboboxes: it = combobox.get_active_iter() model = combobox.get_model() lang_code = model.get(it, 0)[0] new_codes.append(lang_code) return new_codes def __lang_timeout_cb(self, codes): self._lang_sid = 0 self._model.set_languages_list(codes) self.restart_alerts.append('lang') self.needs_restart = True self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() return False
class AboutMe(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts if alerts else set() self._nick_sid = 0 self._color_valid = True self._nick_valid = True self._color = None self._gender = '' self._age = None self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self._color = XoColor(self._model.get_color()) self._original_nick = self._model.get_nick() self._setup_color() self._setup_nick() self._setup_gender() self._setup_age() self._update_pickers(self._color) self._nick_entry.set_text(self._original_nick) self._color_valid = True self._nick_valid = True self.needs_restart = False self._nick_entry.connect('changed', self.__nick_changed_cb) for picker in self._pickers.values(): picker.connect('color-changed', self.__color_changed_cb) self._female_picker.connect('gender-changed', self.__gender_changed_cb) self._male_picker.connect('gender-changed', self.__gender_changed_cb) for picker in self._age_pickers: picker.connect('age-changed', self.__age_changed_cb) def _setup_nick(self): grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._nick_entry = Gtk.Entry() self._nick_entry.set_width_chars(25) grid.attach(self._nick_entry, 0, 0, 1, 1) self._nick_entry.show() alert_grid = Gtk.Grid() self._nick_alert = InlineAlert() alert_grid.attach(self._nick_alert, 0, 0, 1, 1) if 'nick' in self.restart_alerts: self._nick_alert.props.msg = self.restart_msg self._nick_alert.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() center_alert = Gtk.Alignment.new(0.5, 0, 0, 0) center_alert.add(alert_grid) alert_grid.show() self.pack_start(center_in_panel, False, False, 0) self.pack_start(center_alert, False, False, 0) center_in_panel.show() center_alert.show() def _setup_color(self): grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._color_alert = None self._pickers = { _PREVIOUS_FILL_COLOR: ColorPicker(_PREVIOUS_FILL_COLOR), _NEXT_FILL_COLOR: ColorPicker(_NEXT_FILL_COLOR), _CURRENT_COLOR: ColorPicker(_CURRENT_COLOR), _NEXT_STROKE_COLOR: ColorPicker(_NEXT_STROKE_COLOR), _PREVIOUS_STROKE_COLOR: ColorPicker(_PREVIOUS_STROKE_COLOR), } label_color = Gtk.Label(label=_('Click to change your color:')) label_color.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) grid.attach(label_color, 0, 0, 3, 1) label_color.show() current = 0 for picker_index in sorted(self._pickers.keys()): if picker_index == _CURRENT_COLOR: left_separator = Gtk.SeparatorToolItem() grid.attach(left_separator, current, 1, 1, 1) left_separator.show() current += 1 picker = self._pickers[picker_index] picker.show() grid.attach(picker, current, 1, 1, 1) current += 1 if picker_index == _CURRENT_COLOR: right_separator = Gtk.SeparatorToolItem() right_separator.show() grid.attach(right_separator, current, 1, 1, 1) current += 1 label_color_error = Gtk.Label() grid.attach(label_color_error, 0, 2, 3, 1) label_color_error.show() self._color_alert = InlineAlert() grid.attach(self._color_alert, 0, 3, 3, 1) if 'color' in self.restart_alerts: self._color_alert.props.msg = self.restart_msg self._color_alert.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def _setup_gender(self): self._gender = self._model.get_gender() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) label_gender = Gtk.Label(label=_('Select gender:')) label_gender.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) grid.attach(label_gender, 0, 0, 1, 1) label_gender.show() self._female_picker = GenderPicker(self._color, 'female') grid.attach(self._female_picker, 0, 1, 1, 1) self._female_picker.props.gender = self._gender self._female_picker.show() self._male_picker = GenderPicker(self._color, 'male') grid.attach(self._male_picker, 1, 1, 1, 1) self._male_picker.props.gender = self._gender self._male_picker.show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def _setup_age(self): self._age = self._model.get_age() grid = Gtk.Grid() grid.set_row_spacing(style.DEFAULT_SPACING) grid.set_column_spacing(style.DEFAULT_SPACING) self._age_pickers = [] for i in range(len(AGES)): self._age_pickers.append(AgePicker(self._color, self._gender, i)) label_age = Gtk.Label(label=_('Select age:')) label_age.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) grid.attach(label_age, 0, 0, 1, 1) label_age.show() for i in range(len(AGES)): grid.attach(self._age_pickers[i], i, 1, 1, 1) self._age_pickers[i].set_age(self._age) self._age_pickers[i].show() center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) center_in_panel.add(grid) grid.show() self.pack_start(center_in_panel, False, False, 0) center_in_panel.show() def setup(self): pass def undo(self): self._model.undo() self._nick_alert.hide() self._color_alert.hide() self._model.set_gender(self._gender) self._model.set_age(self._age) def _update_pickers(self, color): for picker in self._pickers.values(): picker.props.color = color self._female_picker.set_color(color, self._gender) self._male_picker.set_color(color, self._gender) for i in range(len(AGES)): self._age_pickers[i].set_color(color, self._age) def _validate(self): if self._nick_valid and self._color_valid: self.props.is_valid = True else: self.props.is_valid = False def __nick_changed_cb(self, widget, data=None): if self._nick_sid: GObject.source_remove(self._nick_sid) self._nick_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__nick_timeout_cb, widget) def __nick_timeout_cb(self, widget): self._nick_sid = 0 if widget.get_text() == self._model.get_nick(): self.restart_alerts.remove('nick') if not self.restart_alerts: self.needs_restart = False self._nick_alert.hide() return False try: self._model.set_nick(widget.get_text()) except ValueError, detail: self._nick_alert.props.msg = detail self._nick_valid = False self._nick_alert.show() else:
class Language(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._lang_sid = 0 self._selected_lang_count = 0 self._labels = [] self._language_dict = {} self._country_dict = {} self._language_buttons = [] self._country_buttons = [] self._language_widgets = [] self._country_widgets = [] self._country_codes = [] self._add_remove_boxes = [] self._changed = False self._cursor_change_handler = None self._available_locales = self._model.read_all_languages() self._selected_locales = self._model.get_languages() for language, country, code in self._available_locales: if language not in self._language_dict: self._language_dict[language] = _translate_language(language) self._country_dict[language] = [[code, country]] else: self._country_dict[language].append([code, country]) self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) explanation = gettext.gettext('Add languages in the order you prefer.' ' If a translation is not available,' ' the next in the list will be used.') self._text = Gtk.Label(label=explanation) self._text.set_line_wrap(True) self._text.set_alignment(0, 0) self.pack_start(self._text, False, False, 0) self._text.show() scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled.show() self.pack_start(scrolled, True, True, 0) self._table = Gtk.Table(rows=2, columns=4, homogeneous=False) self._table.set_border_width(style.DEFAULT_SPACING * 2) self._table.show() scrolled.add_with_viewport(self._table) self._lang_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._lang_alert_box, False, True, 0) self._lang_alert = InlineAlert() self._lang_alert_box.pack_start(self._lang_alert, True, True, 0) if 'lang' in self.restart_alerts: self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() self._lang_alert_box.show() self.setup() def _add_row(self, locale_code=None): """Adds two rows to the table: (1) the buttons and labels for language and country; (2) the tables of languages and country options""" self._selected_lang_count += 1 self._table.resize(self._selected_lang_count * 2, 3) label = Gtk.Label(label=str(self._selected_lang_count)) label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._labels.append(label) self._attach_to_table(label, 0, 1, self._selected_lang_count * 2 - 1, xpadding=1, ypadding=1) label.show() locale_language = None locale_country = None if locale_code is not None: for language, country, code in self._available_locales: if code == locale_code: locale_language = language locale_country = country language_palette = [] key_list = self._language_dict.keys() for language_key in sorted(key_list): language_palette.append({ 'label': self._language_dict[language_key], 'index': len(self._language_buttons), 'callback': self._language_changed }) new_language_widget = set_palette_list(language_palette) if locale_language is None: locale_language = 'English' new_language_button = FilterToolItem('go-down', 'go-up', locale_language, new_language_widget) country_list = self._build_country_list(locale_language) new_country_widget = set_palette_list(country_list) if locale_country is None: if locale_language == 'English': locale_country = 'USA' else: locale_country = self._country_dict[locale_language][0] new_country_button = FilterToolItem('go-down', 'go-up', locale_country, new_country_widget) if locale_code is None: # check the locale code acordinig to the default values selected for language, country, code in self._available_locales: if language == locale_language and country == locale_country: locale_code = code self._country_codes.append(locale_code) self._language_buttons.append(new_language_button) self._attach_to_table(new_language_button, 1, 2, self._selected_lang_count * 2 - 1, yoptions=Gtk.AttachOptions.SHRINK) self._language_widgets.append(new_language_widget) self._attach_to_table(new_language_widget, 1, 2, self._selected_lang_count * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) self._country_buttons.append(new_country_button) self._attach_to_table(new_country_button, 2, 3, self._selected_lang_count * 2 - 1, yoptions=Gtk.AttachOptions.SHRINK) self._country_widgets.append(new_country_widget) self._attach_to_table(new_country_widget, 2, 3, self._selected_lang_count * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) add_remove_box = self._create_add_remove_box() self._add_remove_boxes.append(add_remove_box) self._attach_to_table(add_remove_box, 3, 4, self._selected_lang_count * 2 - 1) add_remove_box.show_all() if self._selected_lang_count > 1: previous_add_removes = self._add_remove_boxes[-2] previous_add_removes.hide() # Hide the Remove button if the new added row is the only # language. elif self._selected_lang_count == 1: add_button_, remove_button = add_remove_box.get_children() remove_button.props.visible = False new_language_button.show() new_country_button.show() def _build_country_list(self, language, idx=None): country_list = [] if idx is None: idx = len(self._country_buttons) for country, code in sorted((_translate_country(entry[1]), entry[0]) for entry in self._country_dict[language]): country_list.append({ 'label': country, 'code': code, 'index': idx, 'callback': self._country_changed }) return country_list def _attach_to_table(self, widget, left, right, above, xpadding=style.DEFAULT_SPACING, ypadding=style.DEFAULT_SPACING, yoptions=Gtk.AttachOptions.FILL): self._table.attach(widget, left, right, above, above + 1, xoptions=Gtk.AttachOptions.FILL, yoptions=yoptions, xpadding=xpadding, ypadding=ypadding) def _delete_last_row(self): """Deletes the last two rows of the table""" self._selected_lang_count -= 1 label = self._labels.pop() label.destroy() add_remove_box = self._add_remove_boxes.pop() add_remove_box.destroy() language_button = self._language_buttons.pop() language_button.destroy() country_button = self._country_buttons.pop() country_button.destroy() language_widget = self._language_widgets.pop() language_widget.destroy() country_widget = self._country_widgets.pop() country_widget.destroy() # Remove language code associated with last row self._country_codes.pop() self._table.resize(self._selected_lang_count * 2, 3) self._add_remove_boxes[-1].show_all() # Hide or show the Remove button in the new last row, # depending if it is the only language. add_remove_box = self._add_remove_boxes[-1] add_button_, remove_button = add_remove_box.get_children() if self._selected_lang_count == 1: remove_button.props.visible = False else: remove_button.props.visible = True def setup(self): for locale in self._selected_locales: logging.debug('locale_code=%s' % (locale)) self._add_row(locale_code=locale) def undo(self): self._model.undo() self._lang_alert.hide() def _create_add_remove_box(self): """Creates Gtk.Hbox with add/remove buttons""" add_icon = Icon(icon_name='list-add') add_button = Gtk.Button() add_button.set_image(add_icon) add_button.connect('clicked', self.__add_button_clicked_cb) remove_icon = Icon(icon_name='list-remove') remove_button = Gtk.Button() remove_button.set_image(remove_icon) remove_button.connect('clicked', self.__remove_button_clicked_cb) add_remove_box = Gtk.HButtonBox() add_remove_box.set_layout(Gtk.ButtonBoxStyle.START) add_remove_box.set_spacing(10) add_remove_box.pack_start(add_button, True, True, 0) add_remove_box.pack_start(remove_button, True, True, 0) return add_remove_box def __add_button_clicked_cb(self, button): self._add_row() self._check_change() def __remove_button_clicked_cb(self, button): self._delete_last_row() self._check_change() def _language_changed(self, widget, event, item): i = item['index'] for language_key in self._language_dict.keys(): if self._language_dict[language_key] == item['label']: new_country_list = \ self._build_country_list(language_key, idx=i) break self._language_buttons[i].set_widget_label(item['label']) self._language_buttons[i].button_cb() self._country_buttons[i].set_widget_label( _translate_country(self._country_dict[language_key][0][1])), self._country_codes[i] = self._country_dict[language_key][0][0] old_country_widget = self._country_widgets[i] old_country_widget.destroy() new_country_widget = set_palette_list(new_country_list) self._country_buttons[i].set_widget(new_country_widget) self._country_widgets[i] = new_country_widget self._attach_to_table(new_country_widget, 2, 3, (i + 1) * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) self._update_country(new_country_list[0]) def _country_changed(self, widget, event, item): self._update_country(item) def _update_country(self, item): i = item['index'] self._country_codes[i] = item['code'] self._country_buttons[i].set_widget_label(item['label']) self._country_buttons[i].button_cb() self._check_change() def _check_change(self): selected_langs = self._country_codes[:] self._changed = (selected_langs != self._selected_locales) if self._changed is False: # The user reverted back to the original config self.needs_restart = False if 'lang' in self.restart_alerts: self.restart_alerts.remove('lang') self._lang_alert.hide() if self._lang_sid: GObject.source_remove(self._lang_sid) self._model.undo() return False if self._lang_sid: GObject.source_remove(self._lang_sid) self._lang_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__lang_timeout_cb, selected_langs) def __lang_timeout_cb(self, codes): self._lang_sid = 0 try: self._model.set_languages_list(codes) self.restart_alerts.append('lang') self.needs_restart = True self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() except IOError as e: logging.exception('Error writing i18n config %s', e) self.undo() self._lang_alert.props.msg = gettext.gettext( 'Error writing language configuration (%s)') % e self._lang_alert.show() self.props.is_valid = False return False
class TimeZone(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._zone_sid = 0 self._cursor_change_handler = None self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self.connect('realize', self.__realize_cb) self._entry = iconentry.IconEntry() self._entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY, 'entry-search') self._entry.add_clear_button() self.pack_start(self._entry, False, False, 0) self._entry.show() self._scrolled_window = Gtk.ScrolledWindow() self._scrolled_window.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self._scrolled_window.set_shadow_type(Gtk.ShadowType.IN) self._store = Gtk.ListStore(GObject.TYPE_STRING) zones = model.read_all_timezones() for zone in zones: self._store.append([zone]) self._treeview = Gtk.TreeView(self._store) self._treeview.set_search_entry(self._entry) self._treeview.set_search_equal_func(self._search, None) self._treeview.set_search_column(0) self._scrolled_window.add(self._treeview) self._treeview.show() self._timezone_column = Gtk.TreeViewColumn(_('Timezone')) self._cell = Gtk.CellRendererText() self._timezone_column.pack_start(self._cell, True) self._timezone_column.add_attribute(self._cell, 'text', 0) self._timezone_column.set_sort_column_id(0) self._treeview.append_column(self._timezone_column) self.pack_start(self._scrolled_window, True, True, 0) self._scrolled_window.show() self._zone_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._zone_alert_box, False, False, 0) self._zone_alert = InlineAlert() self._zone_alert_box.pack_start(self._zone_alert, True, True, 0) if 'zone' in self.restart_alerts: self._zone_alert.props.msg = self.restart_msg self._zone_alert.show() self._zone_alert_box.show() self.setup() def setup(self): zone = self._model.get_timezone() for row in self._store: if zone == row[0]: self._treeview.set_cursor(row.path, self._timezone_column, False) self._treeview.scroll_to_cell(row.path, self._timezone_column, True, 0.5, 0.5) break self.needs_restart = False self._cursor_change_handler = self._treeview.connect( 'cursor-changed', self.__zone_changed_cd) def undo(self): self._treeview.disconnect(self._cursor_change_handler) self._model.undo() self._zone_alert.hide() def __realize_cb(self, widget): self._entry.grab_focus() def _search(self, model, column, key, iterator, data=None): value = model.get_value(iterator, column) if key.lower() in value.lower(): return False return True def __zone_changed_cd(self, treeview, data=None): list_, row = treeview.get_selection().get_selected() if not row: return False if self._model.get_timezone() == self._store.get_value(row, 0): return False if self._zone_sid: GObject.source_remove(self._zone_sid) self._zone_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__zone_timeout_cb, row) return True def __zone_timeout_cb(self, row): self._zone_sid = 0 self._model.set_timezone(self._store.get_value(row, 0)) self.restart_alerts.append('zone') self.needs_restart = True self._zone_alert.props.msg = self.restart_msg self._zone_alert.show() return False
class Language(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self.props.is_deferrable = False self._lang_sid = 0 self._selected_lang_count = 0 self._labels = [] self._language_dict = {} self._country_dict = {} self._language_buttons = [] self._country_buttons = [] self._language_widgets = [] self._country_widgets = [] self._country_codes = [] self._add_remove_boxes = [] self._changed = False self._cursor_change_handler = None self._available_locales = self._model.read_all_languages() self._selected_locales = self._model.get_languages() for language, country, code in self._available_locales: if language not in self._language_dict: self._language_dict[language] = _translate_language(language) self._country_dict[language] = [[code, country]] else: self._country_dict[language].append([code, country]) self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) explanation = gettext.gettext('Add languages in the order you prefer.' ' If a translation is not available,' ' the next in the list will be used.') self._text = Gtk.Label(label=explanation) self._text.set_line_wrap(True) self._text.set_alignment(0, 0) self.pack_start(self._text, False, False, 0) self._text.show() scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled.show() self.pack_start(scrolled, True, True, 0) self._table = Gtk.Table(rows=2, columns=4, homogeneous=False) self._table.set_border_width(style.DEFAULT_SPACING * 2) self._table.show() scrolled.add_with_viewport(self._table) self._lang_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._lang_alert_box, False, True, 0) self._lang_alert = InlineAlert() self._lang_alert_box.pack_start(self._lang_alert, True, True, 0) if 'lang' in self.restart_alerts: self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() self._lang_alert_box.show() self.setup() def _add_row(self, locale_code=None): """Adds two rows to the table: (1) the buttons and labels for language and country; (2) the tables of languages and country options""" self._selected_lang_count += 1 self._table.resize(self._selected_lang_count * 2, 3) label = Gtk.Label(label=str(self._selected_lang_count)) label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._labels.append(label) self._attach_to_table(label, 0, 1, self._selected_lang_count * 2 - 1, xpadding=1, ypadding=1) label.show() locale_language = None locale_country = None if locale_code is not None: for language, country, code in self._available_locales: if code == locale_code: locale_language = language locale_country = country for language, country, code in self._available_locales: if code == '.'.join([locale_code, 'utf8']): locale_language = language locale_country = country language_palette = [] key_list = self._language_dict.keys() for language_key in sorted(key_list): language_palette.append( {'label': self._language_dict[language_key], 'index': len(self._language_buttons), 'callback': self._language_changed}) new_language_widget = set_palette_list(language_palette) if locale_language is None: locale_language = 'English' new_language_button = FilterToolItem( 'go-down', 'go-up', locale_language, new_language_widget) country_list = self._build_country_list(locale_language) new_country_widget = set_palette_list(country_list) if locale_country is None: if locale_language == 'English': locale_country = 'USA' else: locale_country = self._country_dict[locale_language][0] new_country_button = FilterToolItem( 'go-down', 'go-up', locale_country, new_country_widget) if locale_code is None: # check the locale code acordinig to the default values selected for language, country, code in self._available_locales: if language == locale_language and country == locale_country: locale_code = code self._country_codes.append(locale_code) self._language_buttons.append(new_language_button) self._attach_to_table( new_language_button, 1, 2, self._selected_lang_count * 2 - 1, yoptions=Gtk.AttachOptions.SHRINK) self._language_widgets.append(new_language_widget) self._attach_to_table(new_language_widget, 1, 2, self._selected_lang_count * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) self._country_buttons.append(new_country_button) self._attach_to_table( new_country_button, 2, 3, self._selected_lang_count * 2 - 1, yoptions=Gtk.AttachOptions.SHRINK) self._country_widgets.append(new_country_widget) self._attach_to_table(new_country_widget, 2, 3, self._selected_lang_count * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) add_remove_box = self._create_add_remove_box() self._add_remove_boxes.append(add_remove_box) self._attach_to_table(add_remove_box, 3, 4, self._selected_lang_count * 2 - 1) add_remove_box.show_all() if self._selected_lang_count > 1: previous_add_removes = self._add_remove_boxes[-2] previous_add_removes.hide() # Hide the Remove button if the new added row is the only # language. elif self._selected_lang_count == 1: add_button_, remove_button = add_remove_box.get_children() remove_button.props.visible = False new_language_button.show() new_country_button.show() def _build_country_list(self, language, idx=None): country_list = [] if idx is None: idx = len(self._country_buttons) for country, code in sorted((_translate_country(entry[1]), entry[0]) for entry in self._country_dict[language]): country_list.append( {'label': country, 'code': code, 'index': idx, 'callback': self._country_changed}) return country_list def _attach_to_table(self, widget, left, right, above, xpadding=style.DEFAULT_SPACING, ypadding=style.DEFAULT_SPACING, yoptions=Gtk.AttachOptions.FILL): self._table.attach(widget, left, right, above, above + 1, xoptions=Gtk.AttachOptions.FILL, yoptions=yoptions, xpadding=xpadding, ypadding=ypadding) def _delete_last_row(self): """Deletes the last two rows of the table""" self._selected_lang_count -= 1 label = self._labels.pop() label.destroy() add_remove_box = self._add_remove_boxes.pop() add_remove_box.destroy() language_button = self._language_buttons.pop() language_button.destroy() country_button = self._country_buttons.pop() country_button.destroy() language_widget = self._language_widgets.pop() language_widget.destroy() country_widget = self._country_widgets.pop() country_widget.destroy() # Remove language code associated with last row self._country_codes.pop() self._table.resize(self._selected_lang_count * 2, 3) if self._selected_lang_count < 1: return self._add_remove_boxes[-1].show_all() # Hide or show the Remove button in the new last row, # depending if it is the only language. add_remove_box = self._add_remove_boxes[-1] add_button_, remove_button = add_remove_box.get_children() if self._selected_lang_count == 1: remove_button.props.visible = False else: remove_button.props.visible = True def setup(self): for locale in self._selected_locales: self._add_row(locale_code=locale) def _delete_all_rows(self): while self._selected_lang_count > 0: self._delete_last_row() def undo(self): self._model.undo() self._lang_alert.hide() self._delete_all_rows() def _create_add_remove_box(self): """Creates Gtk.Hbox with add/remove buttons""" add_icon = Icon(icon_name='list-add') add_button = Gtk.Button() add_button.set_image(add_icon) add_button.connect('clicked', self.__add_button_clicked_cb) remove_icon = Icon(icon_name='list-remove') remove_button = Gtk.Button() remove_button.set_image(remove_icon) remove_button.connect('clicked', self.__remove_button_clicked_cb) add_remove_box = Gtk.HButtonBox() add_remove_box.set_layout(Gtk.ButtonBoxStyle.START) add_remove_box.set_spacing(10) add_remove_box.pack_start(add_button, True, True, 0) add_remove_box.pack_start(remove_button, True, True, 0) return add_remove_box def __add_button_clicked_cb(self, button): self._add_row() self._check_change() def __remove_button_clicked_cb(self, button): self._delete_last_row() self._check_change() def _language_changed(self, widget, event, item): i = item['index'] for language_key in self._language_dict.keys(): if self._language_dict[language_key] == item['label']: new_country_list = \ self._build_country_list(language_key, idx=i) break self._language_buttons[i].set_widget_label(item['label']) self._language_buttons[i].button_cb() self._country_buttons[i].set_widget_label( _translate_country(self._country_dict[language_key][0][1])), self._country_codes[i] = self._country_dict[language_key][0][0] old_country_widget = self._country_widgets[i] old_country_widget.destroy() new_country_widget = set_palette_list(new_country_list) self._country_buttons[i].set_widget(new_country_widget) self._country_widgets[i] = new_country_widget self._attach_to_table(new_country_widget, 2, 3, (i + 1) * 2, xpadding=style.DEFAULT_PADDING, ypadding=0) self._update_country(new_country_list[0]) def _country_changed(self, widget, event, item): self._update_country(item) def _update_country(self, item): i = item['index'] self._country_codes[i] = item['code'] self._country_buttons[i].set_widget_label(item['label']) self._country_buttons[i].button_cb() self._check_change() def _check_change(self): selected_langs = self._country_codes[:] self._changed = (selected_langs != self._selected_locales) if self._changed is False: # The user reverted back to the original config self.needs_restart = False if 'lang' in self.restart_alerts: self.restart_alerts.remove('lang') self._lang_alert.hide() if self._lang_sid: GObject.source_remove(self._lang_sid) self._model.undo() return False if self._lang_sid: GObject.source_remove(self._lang_sid) self._lang_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__lang_timeout_cb, selected_langs) def __lang_timeout_cb(self, codes): self._lang_sid = 0 try: self._model.set_languages_list(codes) self.restart_alerts.append('lang') self.needs_restart = True self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() except IOError as e: logging.exception('Error writing i18n config %s', e) self.undo() self._lang_alert.props.msg = gettext.gettext( 'Error writing language configuration (%s)') % e self._lang_alert.show() self.props.is_valid = False return False
class Network(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._jabber_sid = 0 self._jabber_valid = True self._radio_valid = True self._jabber_change_handler = None self._radio_change_handler = None self._wireless_configuration_reset_handler = None self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) self._radio_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._jabber_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) self.add(scrolled) scrolled.show() workspace = Gtk.VBox() scrolled.add_with_viewport(workspace) workspace.show() separator_wireless = Gtk.HSeparator() workspace.pack_start(separator_wireless, False, True, 0) separator_wireless.show() label_wireless = Gtk.Label(label=_('Wireless')) label_wireless.set_alignment(0, 0) workspace.pack_start(label_wireless, False, True, 0) label_wireless.show() box_wireless = Gtk.VBox() box_wireless.set_border_width(style.DEFAULT_SPACING * 2) box_wireless.set_spacing(style.DEFAULT_SPACING) radio_info = Gtk.Label(label=_('The wireless radio may be turned' ' off to save battery life.')) radio_info.set_alignment(0, 0) radio_info.set_line_wrap(True) radio_info.show() box_wireless.pack_start(radio_info, False, True, 0) box_radio = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._button = Gtk.CheckButton() self._button.set_alignment(0, 0) box_radio.pack_start(self._button, False, True, 0) self._button.show() label_radio = Gtk.Label(label=_('Radio')) label_radio.set_alignment(0, 0.5) box_radio.pack_start(label_radio, False, True, 0) label_radio.show() box_wireless.pack_start(box_radio, False, True, 0) box_radio.show() self._radio_alert = InlineAlert() self._radio_alert_box.pack_start(self._radio_alert, False, True, 0) box_radio.pack_end(self._radio_alert_box, False, True, 0) self._radio_alert_box.show() if 'radio' in self.restart_alerts: self._radio_alert.props.msg = self.restart_msg self._radio_alert.show() wireless_info = Gtk.Label( label=_('Discard wireless connections if' ' you have trouble connecting to the network')) wireless_info.set_alignment(0, 0) wireless_info.set_line_wrap(True) wireless_info.show() box_wireless.pack_start(wireless_info, False, True, 0) box_clear_wireless = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._clear_wireless_button = Gtk.Button() self._clear_wireless_button.set_label( _('Discard wireless connections')) box_clear_wireless.pack_start( self._clear_wireless_button, False, True, 0) if not self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(False) self._clear_wireless_button.show() box_wireless.pack_start(box_clear_wireless, False, True, 0) box_clear_wireless.show() workspace.pack_start(box_wireless, False, True, 0) box_wireless.show() separator_mesh = Gtk.HSeparator() workspace.pack_start(separator_mesh, False, False, 0) separator_mesh.show() label_mesh = Gtk.Label(label=_('Collaboration')) label_mesh.set_alignment(0, 0) workspace.pack_start(label_mesh, False, True, 0) label_mesh.show() box_mesh = Gtk.VBox() box_mesh.set_border_width(style.DEFAULT_SPACING * 2) box_mesh.set_spacing(style.DEFAULT_SPACING) server_info = Gtk.Label(_("The server is the equivalent of what" " room you are in; people on the same server" " will be able to see each other, even when" " they aren't on the same network.")) server_info.set_alignment(0, 0) server_info.set_line_wrap(True) box_mesh.pack_start(server_info, False, True, 0) server_info.show() box_server = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_server = Gtk.Label(label=_('Server:')) label_server.set_alignment(1, 0.5) label_server.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_server.pack_start(label_server, False, True, 0) group.add_widget(label_server) label_server.show() self._entry = Gtk.Entry() self._entry.set_alignment(0) self._entry.set_size_request(int(Gdk.Screen.width() / 3), -1) box_server.pack_start(self._entry, False, True, 0) self._entry.show() box_mesh.pack_start(box_server, False, True, 0) box_server.show() self._jabber_alert = InlineAlert() label_jabber_error = Gtk.Label() group.add_widget(label_jabber_error) self._jabber_alert_box.pack_start(label_jabber_error, False, True, 0) label_jabber_error.show() self._jabber_alert_box.pack_start(self._jabber_alert, False, True, 0) box_mesh.pack_end(self._jabber_alert_box, False, True, 0) self._jabber_alert_box.show() if 'jabber' in self.restart_alerts: self._jabber_alert.props.msg = self.restart_msg self._jabber_alert.show() workspace.pack_start(box_mesh, False, True, 0) box_mesh.show() self.setup() def setup(self): self._entry.set_text(self._model.get_jabber()) try: radio_state = self._model.get_radio() except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_alert.show() else: self._button.set_active(radio_state) self._jabber_valid = True self._radio_valid = True self.needs_restart = False self._radio_change_handler = self._button.connect( 'toggled', self.__radio_toggled_cb) self._jabber_change_handler = self._entry.connect( 'changed', self.__jabber_changed_cb) self._wireless_configuration_reset_handler = \ self._clear_wireless_button.connect( 'clicked', self.__wireless_configuration_reset_cb) def undo(self): self._button.disconnect(self._radio_change_handler) self._entry.disconnect(self._jabber_change_handler) self._model.undo() self._jabber_alert.hide() self._radio_alert.hide() def _validate(self): if self._jabber_valid and self._radio_valid: self.props.is_valid = True else: self.props.is_valid = False def __radio_toggled_cb(self, widget, data=None): radio_state = widget.get_active() try: self._model.set_radio(radio_state) except self._model.ReadError as detail: self._radio_alert.props.msg = detail self._radio_valid = False else: self._radio_valid = True if self._model.have_wireless_networks(): self._clear_wireless_button.set_sensitive(True) self._validate() return False def __jabber_changed_cb(self, widget, data=None): if self._jabber_sid: GObject.source_remove(self._jabber_sid) self._jabber_sid = GObject.timeout_add(_APPLY_TIMEOUT, self.__jabber_timeout_cb, widget) def __jabber_timeout_cb(self, widget): self._jabber_sid = 0 if widget.get_text() == self._model.get_jabber: return try: self._model.set_jabber(widget.get_text()) except self._model.ReadError as detail: self._jabber_alert.props.msg = detail self._jabber_valid = False self._jabber_alert.show() self.restart_alerts.append('jabber') else: self._jabber_valid = True self._jabber_alert.hide() self._validate() return False def __wireless_configuration_reset_cb(self, widget): # FIXME: takes effect immediately, not after CP is closed with # confirmation button self._model.clear_wireless_networks() self._clear_wireless_button.set_sensitive(False)
class AboutMe(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._nick_sid = 0 self._color_valid = True self._nick_valid = True self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self._group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) self._color_label = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._color_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._color_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._color_alert = None self._pickers = { _PREVIOUS_FILL_COLOR: ColorPicker(_PREVIOUS_FILL_COLOR), _NEXT_FILL_COLOR: ColorPicker(_NEXT_FILL_COLOR), _CURRENT_COLOR: ColorPicker(_CURRENT_COLOR), _NEXT_STROKE_COLOR: ColorPicker(_NEXT_STROKE_COLOR), _PREVIOUS_STROKE_COLOR: ColorPicker(_PREVIOUS_STROKE_COLOR), } self._setup_color() initial_color = XoColor(self._model.get_color_xo()) self._update_pickers(initial_color) self._nick_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._nick_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self._nick_entry = None self._nick_alert = None self._setup_nick() self.setup() def _setup_nick(self): self._nick_entry = Gtk.Entry() self._nick_entry.set_width_chars(25) self._nick_box.pack_start(self._nick_entry, False, True, 0) self._nick_entry.show() label_entry_error = Gtk.Label() self._group.add_widget(label_entry_error) self._nick_alert_box.pack_start(label_entry_error, False, True, 0) label_entry_error.show() self._nick_alert = InlineAlert() self._nick_alert_box.pack_start(self._nick_alert, True, True, 0) if 'nick' in self.restart_alerts: self._nick_alert.props.msg = self.restart_msg self._nick_alert.show() self._center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) self._center_in_panel.add(self._nick_box) self.pack_start(self._center_in_panel, False, False, 0) self.pack_start(self._nick_alert_box, False, False, 0) self._nick_box.show() self._nick_alert_box.show() self._center_in_panel.show() def _setup_color(self): label_color = Gtk.Label(label=_('Click to change your color:')) label_color.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._group.add_widget(label_color) self._color_label.pack_start(label_color, False, True, 0) label_color.show() for picker_index in sorted(self._pickers.keys()): if picker_index == _CURRENT_COLOR: left_separator = Gtk.SeparatorToolItem() left_separator.show() self._color_box.pack_start(left_separator, False, True, 0) picker = self._pickers[picker_index] picker.show() self._color_box.pack_start(picker, False, True, 0) if picker_index == _CURRENT_COLOR: right_separator = Gtk.SeparatorToolItem() right_separator.show() self._color_box.pack_start(right_separator, False, True, 0) label_color_error = Gtk.Label() self._group.add_widget(label_color_error) self._color_alert_box.pack_start(label_color_error, False, True, 0) label_color_error.show() self._color_alert = InlineAlert() self._color_alert_box.pack_start(self._color_alert, True, True, 0) if 'color' in self.restart_alerts: self._color_alert.props.msg = self.restart_msg self._color_alert.show() self._center_in_panel = Gtk.Alignment.new(0.5, 0, 0, 0) self._center_in_panel.add(self._color_box) self.pack_start(self._color_label, False, False, 0) self.pack_start(self._center_in_panel, False, False, 0) self.pack_start(self._color_alert_box, False, False, 0) self._color_label.show() self._color_box.show() self._color_alert_box.show() self._center_in_panel.show() def setup(self): self._nick_entry.set_text(self._model.get_nick()) self._color_valid = True self._nick_valid = True self.needs_restart = False self._nick_entry.connect('changed', self.__nick_changed_cb) for picker in self._pickers.values(): picker.connect('color-changed', self.__color_changed_cb) def undo(self): self._model.undo() self._nick_alert.hide() self._color_alert.hide() def _update_pickers(self, color): for picker in self._pickers.values(): picker.update(color) def _validate(self): if self._nick_valid and self._color_valid: self.props.is_valid = True else: self.props.is_valid = False def __nick_changed_cb(self, widget, data=None): if self._nick_sid: GObject.source_remove(self._nick_sid) self._nick_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__nick_timeout_cb, widget) def __nick_timeout_cb(self, widget): self._nick_sid = 0 if widget.get_text() == self._model.get_nick(): return False try: self._model.set_nick(widget.get_text()) except ValueError, detail: self._nick_alert.props.msg = detail self._nick_valid = False else:
class Language(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._lang_sid = 0 self._selected_lang_count = 0 self._labels = [] self._stores = [] self._comboboxes = [] self._add_remove_boxes = [] self._changed = False self._cursor_change_handler = None self._available_locales = self._model.read_all_languages() self._selected_locales = self._model.get_languages() self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) explanation = gettext.gettext('Add languages in the order you prefer.' ' If a translation is not available,' ' the next in the list will be used.') self._text = Gtk.Label(label=explanation) self._text.set_line_wrap(True) self._text.set_alignment(0, 0) self.pack_start(self._text, False, False, 0) self._text.show() scrolled = Gtk.ScrolledWindow() scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC) scrolled.show() self.pack_start(scrolled, True, True, 0) self._table = Gtk.Table(rows=1, columns=3, homogeneous=False) self._table.set_border_width(style.DEFAULT_SPACING * 2) self._table.show() scrolled.add_with_viewport(self._table) self._lang_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._lang_alert_box, False, True, 0) self._lang_alert = InlineAlert() self._lang_alert_box.pack_start(self._lang_alert, True, True, 0) if 'lang' in self.restart_alerts: self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() self._lang_alert_box.show() self.setup() def _add_row(self, locale_code=None): """Adds a row to the table""" self._selected_lang_count += 1 self._table.resize(self._selected_lang_count, 3) label = Gtk.Label(label=str(self._selected_lang_count)) label.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._labels.append(label) self._attach_to_table(label, 0, 1, padding=1) label.show() store = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_STRING) for language, country, code in self._available_locales: description = '%s (%s)' % (_translate_language(language), _translate_country(country)) store.append([code, description]) combobox = Gtk.ComboBox(model=store) cell = Gtk.CellRendererText() combobox.pack_start(cell, True) combobox.add_attribute(cell, 'text', 1) if locale_code: for row in store: lang = locale_code.split('.')[0] lang_column = row[0].split('.')[0] if lang in lang_column: combobox.set_active_iter(row.iter) break else: combobox.set_active(1) combobox.connect('changed', self.__combobox_changed_cb) self._stores.append(store) self._comboboxes.append(combobox) self._attach_to_table( combobox, 1, 2, yoptions=Gtk.AttachOptions.SHRINK) add_remove_box = self._create_add_remove_box() self._add_remove_boxes.append(add_remove_box) self._attach_to_table(add_remove_box, 2, 3) add_remove_box.show_all() if self._selected_lang_count > 1: previous_add_removes = self._add_remove_boxes[-2] previous_add_removes.hide() # Hide the Remove button if the new added row is the only # language. elif self._selected_lang_count == 1: add_button_, remove_button = add_remove_box.get_children() remove_button.props.visible = False combobox.show() def _attach_to_table(self, widget, row, column, padding=20, yoptions=Gtk.AttachOptions.FILL): self._table.attach(widget, row, column, self._selected_lang_count - 1, self._selected_lang_count, xoptions=Gtk.AttachOptions.FILL, yoptions=yoptions, xpadding=padding, ypadding=padding) def _delete_last_row(self): """Deletes the last row of the table""" self._selected_lang_count -= 1 label, add_remove_box, combobox, store_ = self._get_last_row() label.destroy() add_remove_box.destroy() combobox.destroy() self._table.resize(self._selected_lang_count, 3) self._add_remove_boxes[-1].show_all() # Hide or show the Remove button in the new last row, # depending if it is the only language. add_remove_box = self._add_remove_boxes[-1] add_button_, remove_button = add_remove_box.get_children() if self._selected_lang_count == 1: remove_button.props.visible = False else: remove_button.props.visible = True def _get_last_row(self): label = self._labels.pop() add_remove_box = self._add_remove_boxes.pop() combobox = self._comboboxes.pop() store = self._stores.pop() return label, add_remove_box, combobox, store def setup(self): for locale in self._selected_locales: self._add_row(locale_code=locale) def undo(self): self._model.undo() self._lang_alert.hide() def _create_add_remove_box(self): """Creates Gtk.Hbox with add/remove buttons""" add_icon = Icon(icon_name='list-add') add_button = Gtk.Button() add_button.set_image(add_icon) add_button.connect('clicked', self.__add_button_clicked_cb) remove_icon = Icon(icon_name='list-remove') remove_button = Gtk.Button() remove_button.set_image(remove_icon) remove_button.connect('clicked', self.__remove_button_clicked_cb) add_remove_box = Gtk.HButtonBox() add_remove_box.set_layout(Gtk.ButtonBoxStyle.START) add_remove_box.set_spacing(10) add_remove_box.pack_start(add_button, True, True, 0) add_remove_box.pack_start(remove_button, True, True, 0) return add_remove_box def __add_button_clicked_cb(self, button): self._add_row() self._check_change() def __remove_button_clicked_cb(self, button): self._delete_last_row() self._check_change() def __combobox_changed_cb(self, button): self._check_change() def _check_change(self): selected_langs = self._get_selected_langs() self._changed = (selected_langs != self._selected_locales) if self._changed is False: # The user reverted back to the original config self.needs_restart = False if 'lang' in self.restart_alerts: self.restart_alerts.remove('lang') self._lang_alert.hide() if self._lang_sid: GObject.source_remove(self._lang_sid) self._model.undo() return False if self._lang_sid: GObject.source_remove(self._lang_sid) self._lang_sid = GObject.timeout_add(self._APPLY_TIMEOUT, self.__lang_timeout_cb, selected_langs) def _get_selected_langs(self): new_codes = [] for combobox in self._comboboxes: it = combobox.get_active_iter() model = combobox.get_model() lang_code = model.get(it, 0)[0] new_codes.append(lang_code) return new_codes def __lang_timeout_cb(self, codes): self._lang_sid = 0 self._model.set_languages_list(codes) self.restart_alerts.append('lang') self.needs_restart = True self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() return False
class Frame(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self._corner_delay_sid = 0 self._corner_delay_is_valid = True self._corner_delay_change_handler = None self._edge_delay_sid = 0 self._edge_delay_is_valid = True self._edge_delay_change_handler = None self.restart_alerts = alerts self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) self._group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) separator = Gtk.HSeparator() self.pack_start(separator, False, True, 0) separator.show() label_activation = Gtk.Label(label=_('Activation Delay')) label_activation.set_alignment(0, 0) self.pack_start(label_activation, False, True, 0) label_activation.show() self._box_sliders = Gtk.VBox() self._box_sliders.set_border_width(style.DEFAULT_SPACING * 2) self._box_sliders.set_spacing(style.DEFAULT_SPACING) self._corner_delay_slider = None self._corner_delay_alert = None self._setup_corner() self._edge_delay_slider = None self._edge_delay_alert = None self._setup_edge() self.pack_start(self._box_sliders, False, True, 0) self._box_sliders.show() self.setup() def _setup_corner(self): box_delay = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_delay = Gtk.Label(label=_('Corner')) label_delay.set_alignment(1, 0.75) label_delay.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_delay.pack_start(label_delay, False, True, 0) self._group.add_widget(label_delay) label_delay.show() adj = Gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, step_incr=100, page_incr=100, page_size=0) self._corner_delay_slider = Gtk.HScale() self._corner_delay_slider.set_adjustment(adj) self._corner_delay_slider.set_digits(0) self._corner_delay_slider.connect('format-value', self.__corner_delay_format_cb) box_delay.pack_start(self._corner_delay_slider, True, True, 0) self._corner_delay_slider.show() self._box_sliders.pack_start(box_delay, False, True, 0) box_delay.show() self._corner_delay_alert = InlineAlert() label_delay_error = Gtk.Label() self._group.add_widget(label_delay_error) delay_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) delay_alert_box.pack_start(label_delay_error, False, True, 0) label_delay_error.show() delay_alert_box.pack_start(self._corner_delay_alert, False, True, 0) self._box_sliders.pack_start(delay_alert_box, False, True, 0) delay_alert_box.show() if 'corner_delay' in self.restart_alerts: self._corner_delay_alert.props.msg = self.restart_msg self._corner_delay_alert.show() def _setup_edge(self): box_delay = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_delay = Gtk.Label(label=_('Edge')) label_delay.set_alignment(1, 0.75) label_delay.modify_fg(Gtk.StateType.NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) box_delay.pack_start(label_delay, False, True, 0) self._group.add_widget(label_delay) label_delay.show() adj = Gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, step_incr=100, page_incr=100, page_size=0) self._edge_delay_slider = Gtk.HScale() self._edge_delay_slider.set_adjustment(adj) self._edge_delay_slider.set_digits(0) self._edge_delay_slider.connect('format-value', self.__edge_delay_format_cb) box_delay.pack_start(self._edge_delay_slider, True, True, 0) self._edge_delay_slider.show() self._box_sliders.pack_start(box_delay, False, True, 0) box_delay.show() self._edge_delay_alert = InlineAlert() label_delay_error = Gtk.Label() self._group.add_widget(label_delay_error) delay_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) delay_alert_box.pack_start(label_delay_error, False, True, 0) label_delay_error.show() delay_alert_box.pack_start(self._edge_delay_alert, False, True, 0) self._box_sliders.pack_start(delay_alert_box, False, True, 0) delay_alert_box.show() if 'edge_delay' in self.restart_alerts: self._edge_delay_alert.props.msg = self.restart_msg self._edge_delay_alert.show() def setup(self): self._corner_delay_slider.set_value(self._model.get_corner_delay()) self._edge_delay_slider.set_value(self._model.get_edge_delay()) self._corner_delay_is_valid = True self._edge_delay_is_valid = True self.needs_restart = False self._corner_delay_change_handler = self._corner_delay_slider.connect( 'value-changed', self.__corner_delay_changed_cb) self._edge_delay_change_handler = self._edge_delay_slider.connect( 'value-changed', self.__edge_delay_changed_cb) def undo(self): self._corner_delay_slider.disconnect(self._corner_delay_change_handler) self._edge_delay_slider.disconnect(self._edge_delay_change_handler) self._model.undo() self._corner_delay_alert.hide() self._edge_delay_alert.hide() def _validate(self): if self._edge_delay_is_valid and self._corner_delay_is_valid: self.props.is_valid = True else: self.props.is_valid = False def __corner_delay_changed_cb(self, scale, data=None): if self._corner_delay_sid: GObject.source_remove(self._corner_delay_sid) self._corner_delay_sid = GObject.timeout_add( self._APPLY_TIMEOUT, self.__corner_delay_timeout_cb, scale) def __corner_delay_timeout_cb(self, scale): self._corner_delay_sid = 0 if scale.get_value() == self._model.get_corner_delay(): return try: self._model.set_corner_delay(scale.get_value()) except ValueError, detail: self._corner_delay_alert.props.msg = detail self._corner_delay_is_valid = False else:
class Power(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._automatic_pm_valid = True self._automatic_pm_change_handler = None self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL) self._automatic_pm_alert_box = Gtk.HBox(spacing=style.DEFAULT_SPACING) separator_pm = Gtk.HSeparator() self.pack_start(separator_pm, False, True, 0) separator_pm.show() label_pm = Gtk.Label(label=_('Power management')) label_pm.set_alignment(0, 0) self.pack_start(label_pm, False, True, 0) label_pm.show() box_pm = Gtk.VBox() box_pm.set_border_width(style.DEFAULT_SPACING * 2) box_pm.set_spacing(style.DEFAULT_SPACING) box_automatic_pm = Gtk.HBox(spacing=style.DEFAULT_SPACING) label_automatic_pm = Gtk.Label( label=_('Automatic power management (increases battery life)')) label_automatic_pm.set_alignment(0, 0.5) self._automatic_button = Gtk.CheckButton() self._automatic_button.set_alignment(0, 0) box_automatic_pm.pack_start(self._automatic_button, False, True, 0) box_automatic_pm.pack_start(label_automatic_pm, False, True, 0) self._automatic_button.show() label_automatic_pm.show() group.add_widget(label_automatic_pm) box_pm.pack_start(box_automatic_pm, False, True, 0) box_automatic_pm.show() self._automatic_pm_alert = InlineAlert() label_automatic_pm_error = Gtk.Label() group.add_widget(label_automatic_pm_error) self._automatic_pm_alert_box.pack_start(label_automatic_pm_error, expand=False, fill=True, padding=0) label_automatic_pm_error.show() self._automatic_pm_alert_box.pack_start(self._automatic_pm_alert, expand=False, fill=True, padding=0) box_pm.pack_end(self._automatic_pm_alert_box, False, True, 0) self._automatic_pm_alert_box.show() if 'automatic_pm' in self.restart_alerts: self._automatic_pm_alert.props.msg = self.restart_msg self._automatic_pm_alert.show() self.pack_start(box_pm, False, True, 0) box_pm.show() self.setup() def setup(self): try: automatic_state = self._model.get_automatic_pm() except Exception as detail: self._automatic_pm_alert.props.msg = detail self._automatic_pm_alert.show() else: self._automatic_button.set_active(automatic_state) self._automatic_pm_valid = True self.needs_restart = False self._automatic_pm_change_handler = self._automatic_button.connect( 'toggled', self.__automatic_pm_toggled_cb) def undo(self): self._automatic_button.disconnect(self._automatic_pm_change_handler) self._model.undo() self._automatic_pm_alert.hide() def _validate(self): if self._automatic_pm_valid: self.props.is_valid = True else: self.props.is_valid = False def __automatic_pm_toggled_cb(self, widget, data=None): state = widget.get_active() try: self._model.set_automatic_pm(state) except Exception as detail: print(detail) self._automatic_pm_alert.props.msg = detail else: self._automatic_pm_valid = True self._validate() return False
class Language(SectionView): def __init__(self, model, alerts): SectionView.__init__(self) self._model = model self.restart_alerts = alerts self._lang_sid = 0 self._selected_lang_count = 0 self._labels = [] self._stores = [] self._comboboxes = [] self._add_remove_boxes = [] self._changed = False self._cursor_change_handler = None self._available_locales = self._model.read_all_languages() self._selected_locales = self._model.get_languages() self.set_border_width(style.DEFAULT_SPACING * 2) self.set_spacing(style.DEFAULT_SPACING) explanation = gettext.gettext('Add languages in the order you prefer.' ' If a translation is not available,' ' the next in the list will be used.') self._text = gtk.Label(explanation) self._text.set_width_chars(100) self._text.set_line_wrap(True) self._text.set_alignment(0, 0) self.pack_start(self._text, False) self._text.show() scrolled = gtk.ScrolledWindow() scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) scrolled.show() self.pack_start(scrolled, expand=True) self._table = gtk.Table(rows=1, columns=3, homogeneous=False) self._table.set_border_width(style.DEFAULT_SPACING * 2) self._table.show() scrolled.add_with_viewport(self._table) self._lang_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) self.pack_start(self._lang_alert_box, False) self._lang_alert = InlineAlert() self._lang_alert_box.pack_start(self._lang_alert) if 'lang' in self.restart_alerts: self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() self._lang_alert_box.show() self.setup() def _add_row(self, locale_code=None): """Adds a row to the table""" self._selected_lang_count += 1 self._table.resize(self._selected_lang_count, 3) label = gtk.Label(str=str(self._selected_lang_count)) label.modify_fg(gtk.STATE_NORMAL, style.COLOR_SELECTION_GREY.get_gdk_color()) self._labels.append(label) self._attach_to_table(label, 0, 1, padding=1) label.show() store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) for language, country, code in self._available_locales: description = '%s (%s)' % (_translate_language(language), \ _translate_country(country)) store.append([code, description]) combobox = gtk.ComboBox(model=store) cell = gtk.CellRendererText() combobox.pack_start(cell) combobox.add_attribute(cell, 'text', 1) if locale_code: for row in store: lang = locale_code.split('.')[0] lang_column = row[0].split('.')[0] if lang in lang_column: combobox.set_active_iter(row.iter) break else: combobox.set_active(1) combobox.connect('changed', self.__combobox_changed_cb) self._stores.append(store) self._comboboxes.append(combobox) self._attach_to_table(combobox, 1, 2, yoptions=gtk.SHRINK) add_remove_box = self._create_add_remove_box() self._add_remove_boxes.append(add_remove_box) self._attach_to_table(add_remove_box, 2, 3) add_remove_box.show_all() if self._selected_lang_count > 1: previous_add_removes = self._add_remove_boxes[-2] previous_add_removes.hide_all() self._determine_add_remove_visibility() combobox.show() def _attach_to_table(self, widget, row, column, padding=20, \ yoptions=gtk.FILL): self._table.attach(widget, row, column, \ self._selected_lang_count - 1, self._selected_lang_count, \ xoptions=gtk.FILL, yoptions=yoptions, xpadding=padding, \ ypadding=padding) def _delete_last_row(self): """Deletes the last row of the table""" self._selected_lang_count -= 1 label, add_remove_box, combobox, store_ = self._get_last_row() label.destroy() add_remove_box.destroy() combobox.destroy() self._table.resize(self._selected_lang_count, 3) self._add_remove_boxes[-1].show_all() def _get_last_row(self): label = self._labels.pop() add_remove_box = self._add_remove_boxes.pop() combobox = self._comboboxes.pop() store = self._stores.pop() return label, add_remove_box, combobox, store def setup(self): for locale in self._selected_locales: self._add_row(locale_code=locale) def undo(self): self._model.undo() self._lang_alert.hide() def _create_add_remove_box(self): """Creates gtk.Hbox with add/remove buttons""" add_icon = Icon(icon_name='list-add') add_button = gtk.Button() add_button.set_image(add_icon) add_button.connect('clicked', self.__add_button_clicked_cb) remove_icon = Icon(icon_name='list-remove') remove_button = gtk.Button() remove_button.set_image(remove_icon) remove_button.connect('clicked', self.__remove_button_clicked_cb) add_remove_box = gtk.HButtonBox() add_remove_box.set_layout(gtk.BUTTONBOX_START) add_remove_box.set_spacing(10) add_remove_box.pack_start(add_button) add_remove_box.pack_start(remove_button) return add_remove_box def __add_button_clicked_cb(self, button): self._add_row() self._check_change() def __remove_button_clicked_cb(self, button): self._delete_last_row() self._check_change() def __combobox_changed_cb(self, button): self._check_change() def _check_change(self): selected_langs = self._get_selected_langs() last_lang = selected_langs[-1] self._determine_add_remove_visibility(last_lang=last_lang) self._changed = (selected_langs != self._selected_locales) if self._changed == False: # The user reverted back to the original config self.needs_restart = False if 'lang' in self.restart_alerts: self.restart_alerts.remove('lang') self._lang_alert.hide() if self._lang_sid: gobject.source_remove(self._lang_sid) self._model.undo() return False if self._lang_sid: gobject.source_remove(self._lang_sid) self._lang_sid = gobject.timeout_add(self._APPLY_TIMEOUT, self.__lang_timeout_cb, selected_langs) def _get_selected_langs(self): new_codes = [] for combobox in self._comboboxes: it = combobox.get_active_iter() model = combobox.get_model() lang_code = model.get(it, 0)[0] new_codes.append(lang_code) return new_codes def _determine_add_remove_visibility(self, last_lang=None): # We should not let users add fallback languages for English (USA) # This is because the software is not usually _translated_ into English # which means that the fallback gets selected automatically if last_lang is None: selected_langs = self._get_selected_langs() last_lang = selected_langs[-1] add_remove_box = self._add_remove_boxes[-1] buttons = add_remove_box.get_children() add_button, remove_button = buttons if last_lang.startswith('en_US'): add_button.props.visible = False else: add_button.props.visible = True if self._selected_lang_count == 1: remove_button.props.visible = False else: remove_button.props.visible = True def __lang_timeout_cb(self, codes): self._lang_sid = 0 self._model.set_languages_list(codes) self.restart_alerts.append('lang') self.needs_restart = True self._lang_alert.props.msg = self.restart_msg self._lang_alert.show() return False