Ticket #3070: proxy_gsettings.patch

File proxy_gsettings.patch, 16.1 KB (added by manuq, 8 years ago)

Patch that uses GSettings exclusively. Based on patches from Sasha and Ajay

  • extensions/cpsection/network/view.py

    diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
    index b360759..0d230b2 100644
    a b  
    1313# You should have received a copy of the GNU General Public License
    1414# along with this program; if not, write to the Free Software
    1515# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
     16from gettext import gettext as _
     17import logging
    1618
    1719from gi.repository import Gtk
    1820from gi.repository import Gdk
    1921from gi.repository import GObject
    20 from gettext import gettext as _
     22from gi.repository import Gio
     23from gi.repository import GLib
     24from gi.repository import Pango
    2125
    2226from sugar3.graphics import style
    2327
    TITLE = _('Network') 
    3236_APPLY_TIMEOUT = 3000
    3337
    3438
     39class GSettingsMixin(object):
     40    """
     41    Mix-in class for GTK widgets backed by GSettings.
     42    """
     43    def __init__(self, setting_schema, setting_key, widget=None,
     44                 signal='changed'):
     45        self._timeout_id = None
     46        self._gsettings = Gio.Settings.new(setting_schema)
     47        self._setting_key = setting_key
     48
     49        # Get the GSettings type for the key in the format of the XML,
     50        # for example 's' for string.
     51        gvalue = self._gsettings.get_value(self._setting_key)
     52        self._setting_key_type = gvalue.get_type_string()
     53
     54        self._gsettings.connect('changed', self.__settings_changed_cb)
     55        initial_value = self._get_gsettings_value()
     56        self._undo_value = initial_value
     57        self.set_value_from_gsettings(initial_value)
     58        widget = widget or self
     59        widget.connect(signal, self.__changed_cb)
     60
     61    def undo(self):
     62        """
     63        Revert to original value.
     64        """
     65        self._set_gsettings_value(self._undo_value)
     66
     67    def get_value_for_gsettings(self):
     68        """
     69        Apply the current value of the widget to GSettings.
     70
     71        MUST be implemented by subclasses.
     72        """
     73        raise NotImplementedError()
     74
     75    def set_value_from_gsettings(self, value):
     76        """
     77        Set the current value of the widget from GSettings.
     78
     79        MUST be implemented by subclasses.
     80        """
     81        raise NotImplementedError()
     82
     83    def __settings_changed_cb(self, settings, key):
     84        if key != self._setting_key:
     85            return
     86        value = self._get_gsettings_value()
     87        self.set_value_from_gsettings(value)
     88
     89    def __changed_cb(self, widget):
     90        if self._timeout_id is not None:
     91            GLib.source_remove(self._timeout_id)
     92
     93        self._timeout_id = GLib.timeout_add(_APPLY_TIMEOUT, self._commit,
     94                                            widget)
     95
     96    def _commit(self, widget):
     97        new_value = self.get_value_for_gsettings()
     98        logging.debug('Setting %r %r to %r', self._gsettings.props.path,
     99                      self._setting_key, new_value)
     100
     101        widget.handler_block_by_func(self.__changed_cb)
     102        try:
     103            self._set_gsettings_value(new_value)
     104        finally:
     105            widget.handler_unblock_by_func(self.__changed_cb)
     106
     107    def _get_gsettings_value(self):
     108        return self._gsettings.get_value(self._setting_key).unpack()
     109
     110    def _set_gsettings_value(self, value):
     111        if self._setting_key_type == 's':
     112            self._gsettings.set_string(self._setting_key, value)
     113        elif self._setting_key_type == 'i':
     114            self._gsettings.set_int(self._setting_key, value)
     115        elif self._setting_key_type == 'b':
     116            self._gsettings.set_boolean(self._setting_key, value)
     117        else:
     118            raise TypeError('Cannot set %r with type %r to GSettings' %
     119                            (self._setting_key, self._setting_key_type))
     120
     121
     122class GSettingsEntry(Gtk.Entry, GSettingsMixin):
     123    """
     124    Text entry backed by GSettings
     125    """
     126    def __init__(self, setting_schema, setting_key):
     127        Gtk.Entry.__init__(self)
     128        GSettingsMixin.__init__(self, setting_schema, setting_key)
     129
     130    def get_value_for_gsettings(self):
     131        return self.props.text
     132
     133    def set_value_from_gsettings(self, value):
     134        self.props.text = value
     135
     136
     137class GSettingsIntegerSpinButton(Gtk.SpinButton, GSettingsMixin):
     138    """
     139    Integer SpinButton backed by GSettings
     140    """
     141    def __init__(self, setting_schema, setting_key, adjustment, climb_rate=0):
     142        Gtk.SpinButton.__init__(self, adjustment=adjustment,
     143                                climb_rate=climb_rate)
     144        GSettingsMixin.__init__(self, setting_schema, setting_key)
     145
     146    def get_value_for_gsettings(self):
     147        return self.get_value_as_int()
     148
     149    def set_value_from_gsettings(self, value):
     150        self.set_value(value)
     151
     152
     153class SettingBox(Gtk.HBox):
     154    """
     155    Base class for "lines" on the screen representing configuration
     156    settings.
     157    """
     158    def __init__(self, name, size_group=None):
     159        Gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
     160        label = Gtk.Label(name)
     161        label.modify_fg(Gtk.StateType.NORMAL,
     162                        style.COLOR_SELECTION_GREY.get_gdk_color())
     163        label.set_alignment(1, 0.5)
     164        if size_group is not None:
     165            size_group.add_widget(label)
     166        self.pack_start(label, False, False, 0)
     167        label.show()
     168
     169
     170class ComboSettingBox(Gtk.VBox, GSettingsMixin):
     171    """
     172    Container for sets of different settings selected by a top-level
     173    setting.
     174
     175    Renders the top level setting as a ComboBox.  Only the currently
     176    active set is shown on screen.
     177    """
     178    def __init__(self, name, setting_schema, setting_key,
     179                 option_sets, size_group=None):
     180        Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
     181
     182        setting_box = SettingBox(name, size_group)
     183        self.pack_start(setting_box, False, False, 0)
     184        setting_box.show()
     185
     186        display_sets = [(name, widget) for name, value, widget in option_sets]
     187        self._options_mapping = dict([(name, value)
     188                                      for name, value, widget in option_sets])
     189
     190        model = Gtk.ListStore(GObject.TYPE_STRING, GObject.TYPE_OBJECT)
     191        self._combobox = Gtk.ComboBox(model=model)
     192        self._combobox.connect('changed', self.__combo_changed_cb)
     193        setting_box.pack_start(self._combobox, True, True, 0)
     194        self._combobox.show()
     195
     196        cell_renderer = Gtk.CellRendererText()
     197        cell_renderer.props.ellipsize = Pango.EllipsizeMode.MIDDLE
     198        cell_renderer.props.ellipsize_set = True
     199        self._combobox.pack_start(cell_renderer, True)
     200        self._combobox.add_attribute(cell_renderer, 'text', 0)
     201
     202        self._settings_box = Gtk.VBox()
     203        self._settings_box.show()
     204        self.pack_start(self._settings_box, False, False, 0)
     205
     206        for name, box in display_sets:
     207            model.append((name, box))
     208
     209        GSettingsMixin.__init__(self, setting_schema, setting_key,
     210                                self._combobox)
     211
     212    def __combo_changed_cb(self, combobox):
     213        giter = combobox.get_active_iter()
     214        new_box = combobox.get_model().get(giter, 1)[0]
     215        current_box = self._settings_box.get_children()
     216        if current_box:
     217            self._settings_box.remove(current_box[0])
     218
     219        self._settings_box.add(new_box)
     220        new_box.show()
     221
     222    def get_value_for_gsettings(self):
     223        giter = self._combobox.get_active_iter()
     224        if giter is None:
     225            return 'none'
     226        name = self._combobox.get_model().get(giter, 0)[0]
     227        return self._options_mapping[name]
     228
     229    def set_value_from_gsettings(self, value):
     230        for idx, (name, widget_) in enumerate(self._combobox.get_model()):
     231            if self._options_mapping[name] == value:
     232                self._combobox.set_active(idx)
     233                return
     234
     235        raise ValueError('Invalid value %r' % (value, ))
     236
     237
     238class OptionalSettingsBox(Gtk.VBox, GSettingsMixin):
     239    """
     240    Container for settings (de)activated by a top-level setting.
     241
     242    Renders the top level setting as a CheckButton. The settings are only
     243    shown on screen if the top-level setting is enabled.
     244    """
     245    def __init__(self, name, setting_schema, setting_key, contents_box):
     246        Gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
     247
     248        self._contents_box = contents_box
     249
     250        self._check_button = Gtk.CheckButton()
     251        self._check_button.props.label = name
     252        self._check_button.connect('toggled', self.__button_changed_cb)
     253        self._check_button.show()
     254        self.pack_start(self._check_button, True, True, 0)
     255
     256        self.pack_start(contents_box, False, False, 0)
     257
     258        GSettingsMixin.__init__(self, setting_schema, setting_key,
     259                                self._check_button, signal='toggled')
     260
     261    def __button_changed_cb(self, check_button):
     262        self._contents_box.set_visible(check_button.get_active())
     263
     264    def get_value_for_gsettings(self):
     265        return self._check_button.get_active()
     266
     267    def set_value_from_gsettings(self, value):
     268        self._check_button.set_active(value)
     269
     270
     271class HostPortSettingBox(SettingBox):
     272    """
     273    A configuration line for a combined host name and port setting.
     274    """
     275    def __init__(self, name, setting_schema, size_group=None):
     276        SettingBox.__init__(self, name, size_group)
     277
     278        self._host_name_entry = GSettingsEntry(setting_schema, 'host')
     279        self.pack_start(self._host_name_entry, True, True, 0)
     280        self._host_name_entry.show()
     281
     282        # port number 0 means n/a
     283        adjustment = Gtk.Adjustment(0, 0, 65535, 1, 10)
     284        self._port_spin_button = GSettingsIntegerSpinButton(
     285            setting_schema, 'port', adjustment=adjustment, climb_rate=0.1)
     286        self.pack_start(self._port_spin_button, False, False, 0)
     287        self._port_spin_button.show()
     288
     289    def undo(self):
     290        self._host_name_entry.undo()
     291        self._port_spin_button.undo()
     292
     293
     294class StringSettingBox(SettingBox, GSettingsMixin):
     295    """
     296    A configuration line for a string setting.
     297    """
     298    def __init__(self, name, setting_schema, setting_key, size_group=None):
     299        SettingBox.__init__(self, name, size_group)
     300
     301        self._string_entry = Gtk.Entry()
     302        self.pack_start(self._string_entry, True, True, 0)
     303        self._string_entry.show()
     304
     305        GSettingsMixin.__init__(self, setting_schema, setting_key,
     306                                self._string_entry)
     307
     308    def get_value_for_gsettings(self):
     309        return self._string_entry.props.text
     310
     311    def set_value_from_gsettings(self, value):
     312        self._string_entry.props.text = value
     313
     314
    35315class Network(SectionView):
    36316    def __init__(self, model, alerts):
    37317        SectionView.__init__(self)
    class Network(SectionView): 
    44324        self._jabber_change_handler = None
    45325        self._radio_change_handler = None
    46326        self._network_configuration_reset_handler = None
     327        self._undo_settings = []
    47328
    48329        self.set_border_width(style.DEFAULT_SPACING * 2)
    49330        self.set_spacing(style.DEFAULT_SPACING)
    class Network(SectionView): 
    174455        workspace.pack_start(box_mesh, False, True, 0)
    175456        box_mesh.show()
    176457
     458        separator_proxy = Gtk.HSeparator()
     459        workspace.pack_start(separator_proxy, False, False, 0)
     460        separator_proxy.show()
     461
     462        self._add_proxy_section(workspace)
     463
    177464        self.setup()
    178465
     466    def _add_proxy_section(self, workspace):
     467        label_proxy = Gtk.Label(_('Proxy'))
     468        label_proxy.set_alignment(0, 0)
     469        workspace.pack_start(label_proxy, False, True, 0)
     470        label_proxy.show()
     471
     472        box_proxy = Gtk.VBox()
     473        box_proxy.set_border_width(style.DEFAULT_SPACING * 2)
     474        box_proxy.set_spacing(style.DEFAULT_SPACING)
     475        workspace.pack_start(box_proxy, False, True, 0)
     476        box_proxy.show()
     477
     478        size_group = Gtk.SizeGroup(Gtk.SizeGroupMode.HORIZONTAL)
     479
     480        automatic_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     481        manual_proxy_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     482
     483        option_sets = [('None', 'none', Gtk.VBox()),
     484                        ('Automatic', 'auto', automatic_proxy_box),
     485                        ('Manual', 'manual', manual_proxy_box)]
     486
     487        box_mode = ComboSettingBox(_('Method:'), 'org.gnome.system.proxy',
     488                                   'mode', option_sets, size_group)
     489        self._undo_settings.append(box_mode)
     490        box_proxy.pack_start(box_mode, False, False, 0)
     491        box_mode.show()
     492
     493        url_box = StringSettingBox(_('Configuration URL:'),
     494                                   'org.gnome.system.proxy', 'autoconfig-url',
     495                                   size_group)
     496        self._undo_settings.append(url_box)
     497        automatic_proxy_box.pack_start(url_box, True, True, 0)
     498        url_box.show()
     499
     500        wpad_help_text = _('Web Proxy Autodiscovery (WPAD) is used when a'
     501                           ' Configuration URL is not provided. This is not'
     502                           ' recommended for untrusted public networks.')
     503        automatic_proxy_help = Gtk.Label(wpad_help_text)
     504        automatic_proxy_help.set_alignment(0, 0)
     505        automatic_proxy_help.set_line_wrap(True)
     506        automatic_proxy_help.show()
     507        automatic_proxy_box.pack_start(automatic_proxy_help, True, True, 0)
     508
     509        box_http = HostPortSettingBox(_('HTTP Proxy:'),
     510                                      'org.gnome.system.proxy.http',
     511                                      size_group)
     512        self._undo_settings.append(box_http)
     513        manual_proxy_box.pack_start(box_http, False, False, 0)
     514        box_http.show()
     515
     516        auth_contents_box = Gtk.VBox(spacing=style.DEFAULT_SPACING)
     517
     518        auth_box = OptionalSettingsBox(_('Use authentication'),
     519                                       'org.gnome.system.proxy.http',
     520                                       'use-authentication',
     521                                       auth_contents_box)
     522        self._undo_settings.append(auth_box)
     523        manual_proxy_box.pack_start(auth_box, False, False, 0)
     524        auth_box.show()
     525
     526        box_username = StringSettingBox(_('Username:'),
     527                                        'org.gnome.system.proxy.http',
     528                                        'authentication-user',
     529                                        size_group)
     530        self._undo_settings.append(box_username)
     531        auth_contents_box.pack_start(box_username, False, False, 0)
     532        box_username.show()
     533
     534        box_password = StringSettingBox(_('Password:'),
     535                                        'org.gnome.system.proxy.http',
     536                                        'authentication-password',
     537                                        size_group)
     538        self._undo_settings.append(box_password)
     539        auth_contents_box.pack_start(box_password, False, False, 0)
     540        box_password.show()
     541
     542        box_https = HostPortSettingBox(_('HTTPS Proxy:'),
     543                                      'org.gnome.system.proxy.https',
     544                                       size_group)
     545        self._undo_settings.append(box_https)
     546        manual_proxy_box.pack_start(box_https, False, False, 0)
     547        box_https.show()
     548
     549        box_ftp = HostPortSettingBox(_('FTP Proxy:'),
     550                                     'org.gnome.system.proxy.ftp',
     551                                     size_group)
     552        self._undo_settings.append(box_ftp)
     553        manual_proxy_box.pack_start(box_ftp, False, False, 0)
     554        box_ftp.show()
     555
     556        box_socks = HostPortSettingBox(_('SOCKS Proxy:'),
     557                                      'org.gnome.system.proxy.socks',
     558                                       size_group)
     559        self._undo_settings.append(box_socks)
     560        manual_proxy_box.pack_start(box_socks, False, False, 0)
     561        box_socks.show()
     562
    179563    def setup(self):
    180564        self._entry.set_text(self._model.get_jabber())
    181565        try:
    class Network(SectionView): 
    203587        self._model.undo()
    204588        self._jabber_alert.hide()
    205589        self._radio_alert.hide()
     590        for setting in self._undo_settings:
     591            setting.undo()
    206592
    207593    def _validate(self):
    208594        if self._jabber_valid and self._radio_valid: