Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAjay Garg <ajay@activitycentral.com>2012-02-19 20:18:07 (GMT)
committer Anish Mangal <anish@activitycentral.com>2012-04-27 10:02:36 (GMT)
commit1441e68e5818db835de2c7a4139fbcf972e19545 (patch)
treeb473d857bc6263e465e4bd19948784dfac9b0298
parent9a0feb930318de585369e4430f8862d8cbc133e6 (diff)
Porting Sascha's proxy patches to dextrose-3.
The (ported) patch links are :: https://patchwork.sugarlabs.org/patch/1187/ https://patchwork.sugarlabs.org/patch/1188/ https://patchwork.sugarlabs.org/patch/1187/ ------------------------------------------ Both individual users and deployments need to be able to set a proxy for Sugar and activities to use. While we'd like the system to work that all out automatically (e.g. using WPAD [1]), this often isn't possible. Common reasons include legacy ("inherited") setups and network uplinks simply being out of control of the user respectively deployment. The existing Network Control Panel is enhanced by adding a new section for the proxy settings. For consistency between Sugar and Gnome, the basic layout of the Gnome 3 proxy settings has been mirrored: A combo box allows the user to select how the proxy setting should be determined (None=direct connection, Automatic=WPAD or PAC, Manual=enter host names and ports for each protocol). Based on which method was selected, additional configuration options are presented to the user. The settings are stored via gconf, using the same keys as Gnome 2 [2]. This implements the Proxy Settings Feature [3]. [1] https://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol [2] http://people.gnome.org/~bmsmith/gconf-docs/C/gnome-vfs.html [3] https://wiki.sugarlabs.org/go/Features/Proxy_Settings Signed-off-by: Sascha Silbe <silbe@activitycentral.com>
-rwxr-xr-xbin/sugar-session19
-rw-r--r--extensions/cpsection/network/view.py821
2 files changed, 486 insertions, 354 deletions
diff --git a/bin/sugar-session b/bin/sugar-session
index 248810c..bf140f6 100755
--- a/bin/sugar-session
+++ b/bin/sugar-session
@@ -212,7 +212,6 @@ def setup_accessibility_cb():
accessibility_manager = accessibility.AccessibilityManager()
accessibility_manager.setup_accessibility()
-
def export_proxy_settings():
"""Export manual proxy settings from GConf as environment variables
@@ -221,19 +220,25 @@ def export_proxy_settings():
(GConf) proxy settings.
"""
client = gconf.client_get_default()
- if client.get_string('/system/proxy/mode') != 'auto':
+
+ # Note: See https://dev.laptop.org.au/issues/1179#note-9
+ if client.get_string('/system/proxy/mode') == 'none':
return
http_host = client.get_string('/system/http_proxy/host')
http_port = client.get_int('/system/http_proxy/port')
use_auth = client.get_bool('/system/http_proxy/use_authentication')
- proxy_info = '%s:%d' % (http_host, http_port)
if use_auth:
user = client.get_string('/system/http_proxy/authentication_user')
- pword = client.get_string('/system/http_proxy/authentication_password')
- proxy_info = '%s:%s@%s' % (user, pword, proxy_info)
-
- os.environ['http_proxy'] = 'http://%s/' % proxy_info
+ pw = client.get_string('/system/http_proxy/authentication_password')
+ http_proxy = 'http://%s:%s@%s:%d/' % (user, pw, http_host, http_port)
+ else:
+ http_proxy = 'http://%s:%d/' % (http_host, http_port)
+
+ os.environ['http_proxy'] = http_proxy
+ ignore_hosts = client.get_list('/system/http_proxy/ignore_hosts',
+ gconf.VALUE_STRING)
+ os.environ['no_proxy'] = ','.join(ignore_hosts)
def main():
diff --git a/extensions/cpsection/network/view.py b/extensions/cpsection/network/view.py
index a7d723f..5bfe01c 100644
--- a/extensions/cpsection/network/view.py
+++ b/extensions/cpsection/network/view.py
@@ -14,9 +14,14 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import gtk
-import gobject
+
from gettext import gettext as _
+import logging
+
+import gconf
+import gobject
+import gtk
+import pango
from sugar.graphics import style
from sugar.graphics.alert import Alert
@@ -54,397 +59,389 @@ class WrappedLabel(gtk.Label):
widget.set_size_request(rect.width, -1)
+class GConfMixin(object):
+ """Mix-in class for GTK widgets backed by GConf
-# "Proxy" feature merged into "Network"
-#################################################################################################################
+ It is the callers responsibility to call GConfClient.add_dir() for the
+ GConf directory containing the key.
+ """
-# Copyright (C) 2011, Aleksey Lim
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
+ def __init__(self, gconf_key, widget=None, signal='changed'):
+ self._timeout_id = None
+ self._gconf_key = gconf_key
+ client = gconf.client_get_default()
+ self._notify_id = client.notify_add(gconf_key, self.__gconf_notify_cb)
+ initial_value = self._get_gconf_value()
+ self._undo_value = initial_value
+ self.set_value_from_gconf(initial_value)
+ widget = widget or self
+ widget.connect(signal, self.__changed_cb)
-from gettext import gettext as _
-import re
+ def undo(self):
+ """Revert to original value if modified"""
+ if not self.changed:
+ return
-import gconf
+ logging.debug('Reverting %r to %r', self._gconf_key, self._undo_value)
+ self._set_gconf_value(self._undo_value)
-_widget_sensitivies = {}
-_gconf_origin_values = {}
+ def get_value_for_gconf(self):
+ """
+ Return the current value of the widget in a format suitable for GConf
+ MUST be implemented by subclasses.
+ """
+ raise NotImplementedError()
-class Proxy(SectionView):
+ def set_value_from_gconf(self, value):
+ """
+ Set the current value of the widget based on a value from GConf
- def __init__(self):
- SectionView.__init__(self)
- self.set_border_width(style.DEFAULT_SPACING * 2)
- self.set_spacing(style.DEFAULT_SPACING)
- self.setup()
-
- def setup(self):
- for i in self.get_children():
- self.remove(i)
- # Destroy all widgets and connection to avoid any interfering
- i.destroy()
+ MUST be implemented by subclasses.
+ """
+ raise NotImplementedError()
- _widget_sensitivies.clear()
+ def __changed_cb(self, widget):
+ if self._timeout_id is not None:
+ gobject.source_remove(self._timeout_id)
- workspace = gtk.VBox()
- workspace.show()
- self.add(workspace)
-
- def add_section(section, label_text):
- separator = gtk.HSeparator()
- separator.show()
- workspace.pack_start(separator, expand=False)
+ self._timeout_id = gobject.timeout_add(_APPLY_TIMEOUT, self._commit,
+ widget)
- label = gtk.Label(label_text)
- label.set_alignment(0, 0)
- label.show()
- workspace.pack_start(label, expand=False)
+ def __gconf_notify_cb(self, client, transaction_id_, entry, user_data_):
+ new_value = _gconf_value_to_python(entry.value)
+ self.set_value_from_gconf(new_value)
- section.set_border_width(style.DEFAULT_SPACING * 2)
- section.show()
- workspace.pack_start(section, expand=False)
+ def _commit(self, widget):
+ new_value = self.get_value_for_gconf()
+ logging.debug('Setting %r to %r', self._gconf_key, new_value)
- add_section(_ProxySection(),
- _('Configure proxies to access the internet'))
- add_section(_IgnoreSection(), _('Ignore host list'))
+ widget.handler_block_by_func(self.__changed_cb)
+ try:
+ self._set_gconf_value(new_value)
+ finally:
+ widget.handler_unblock_by_func(self.__changed_cb)
+
+ def _set_gconf_value(self, new_value):
+ client = gconf.client_get_default()
+ gconf_type = client.get(self._gconf_key).type
+ if gconf_type == gconf.VALUE_STRING:
+ client.set_string(self._gconf_key, new_value)
+ elif gconf_type == gconf.VALUE_INT:
+ client.set_int(self._gconf_key, new_value)
+ elif gconf_type == gconf.VALUE_FLOAT:
+ client.set_float(self._gconf_key, new_value)
+ elif gconf_type == gconf.VALUE_BOOL:
+ client.set_bool(self._gconf_key, new_value)
+ elif gconf_type == gconf.VALUE_LIST:
+ list_type = client.get(self._gconf_key).get_list_type()
+ client.set_list(self._gconf_key, list_type, new_value)
+ else:
+ raise TypeError('Cannot store %r in GConf' % (new_value, ))
- def undo(self):
- conf = gconf.client_get_default()
- for key, value in _gconf_origin_values.items():
- if value is None:
- conf.unset(key)
- else:
- conf.set(key, value)
+ def _get_gconf_value(self):
+ client = gconf.client_get_default()
+ return _gconf_value_to_python(client.get(self._gconf_key))
@property
- def needs_restart(self):
- conf = gconf.client_get_default()
- for key, value in _gconf_origin_values.items():
- if ((value is None and conf.get_without_default(key) is not None) or
- (value is not None and value.to_string() != conf.get(key).to_string())):
- return True
+ def changed(self):
+ return self._undo_value != self.get_value_for_gconf()
- return False
- @needs_restart.setter
- def needs_restart(self, value):
- # needs_restart is a property (i.e. gets calculated) in this Control
- # Panel, but SectionView.__init__() wants to initialise it to False,
- # so we need to provide a (fake) setter.
- pass
+class GConfEntry(gtk.Entry, GConfMixin):
+ """Text entry backed by GConf
+ It is the callers responsibility to call GConfClient.add_dir() for the
+ GConf directory containing the key.
+ """
-class _ProxySection(gtk.VBox):
+ def __init__(self, gconf_key):
+ gtk.Entry.__init__(self)
+ GConfMixin.__init__(self, gconf_key)
- def __init__(self):
- gtk.VBox.__init__(self)
- self._common_hosts = {}
- self._common_ports = {}
-
- group = gtk.RadioButton()
- group.props.label = _('Direct internet connection')
- group.show()
- self.pack_start(group, expand=False)
- _register_selector_key('/system/proxy/mode', group, 'none')
- _register_bool_key('/system/http_proxy/use_http_proxy', group, True)
-
- manual_proxy = gtk.RadioButton(group)
- manual_proxy.props.label = _('Manual proxy configuration')
- manual_proxy.show()
- self.pack_start(manual_proxy, expand=False)
- _register_selector_key('/system/proxy/mode', manual_proxy, 'manual')
-
- widgets = self._add_protos()
- manual_proxy.connect('toggled', _set_sensitive, False, widgets)
- _set_sensitive(manual_proxy, False, widgets)
-
- auto_proxy = gtk.RadioButton(group)
- auto_proxy.props.label = _('Automatic proxy configuration')
- auto_proxy.show()
- self.pack_start(auto_proxy, expand=False)
- _register_selector_key('/system/proxy/mode', auto_proxy, 'auto')
-
- grid = self._sub_section_new()
- grid.attach_label(_('Autoconfiguration URL (leave empty to use WPAD):'),
- 0, 1, 0, 1)
- entry = grid.attach_entry(1, 2, 0, 1)
- _register_string_key('/system/proxy/autoconfig_url', entry)
- auto_proxy.connect('toggled', _set_sensitive, False, [grid])
- _set_sensitive(auto_proxy, False, [grid])
-
- print 'aa toh raha hai'
-
- def _add_protos(self):
- commons = gtk.CheckButton()
- commons.props.label = _('Use the same proxy for all protocols')
- commons.show()
- self.pack_start(commons)
- _register_bool_key('/system/http_proxy/use_same_proxy', commons)
-
- grid = self._sub_section_new()
-
- def add_proto(row, is_common, label_text, host_key, port_key):
- host_label = grid.attach_label(label_text, 0, 1, row, row + 1)
- host = grid.attach_entry(1, 2, row, row + 1)
-
- port_label = grid.attach_label(_('Port:'), 2, 3, row, row + 1)
- port_value = gtk.Adjustment(8080, 0, 65536, 1, 10)
- port = gtk.SpinButton()
- port.configure(port_value, .1, 0)
- port.show()
- grid.attach(port, 3, 4, row, row + 1,
- gtk.SHRINK | gtk.FILL, gtk.SHRINK)
-
- if is_common:
- _widget_sensitivies.update([
- (host_label, None), (host, None),
- (port_label, None), (port, None)])
- self._common_hosts[host] = host.props.buffer
- self._common_ports[port] = port.props.adjustment
-
- _register_string_key(host_key, host)
- _register_int_key(port_key, port)
-
- return host, port
-
- http_host, http_port = add_proto(1, False, _('HTTP proxy:'),
- '/system/http_proxy/host', '/system/http_proxy/port')
-
- auth_widget = _AuthWidget()
- auth_widget.show()
- grid.attach(auth_widget, 1, 2, 2, 3, gtk.SHRINK | gtk.FILL, gtk.SHRINK)
-
- add_proto(3, True, _('Secure HTTP proxy:'),
- '/system/proxy/secure_host', '/system/proxy/secure_port')
- add_proto(4, True, _('FTP proxy:'),
- '/system/proxy/ftp_host', '/system/proxy/ftp_port')
- add_proto(5, True, _('Socks proxy:'),
- '/system/proxy/socks_host', '/system/proxy/socks_port')
-
- def commons_toggled_cb(sender):
- for widget in _widget_sensitivies.keys():
- _widget_sensitivies[widget] = not sender.props.active
- _set_sensitive(sender, True, _widget_sensitivies.keys())
-
- for widget, orig_buffer in self._common_hosts.items():
- widget.props.buffer = http_host.props.buffer if \
- sender.props.active else orig_buffer
-
- for widget, orig_adjustment in self._common_ports.items():
- widget.props.adjustment = http_port.props.adjustment if \
- sender.props.active else orig_adjustment
- widget.props.value = widget.props.adjustment.value
-
- commons.connect('toggled', commons_toggled_cb)
- commons_toggled_cb(commons)
-
- return [commons, grid]
-
- def _sub_section_new(self):
- grid = _Grid(1, 1, False)
- grid.props.column_spacing = style.DEFAULT_SPACING
- grid.props.row_spacing = style.DEFAULT_SPACING
- grid.show()
-
- alignment = gtk.Alignment(0, 0, 1, 1)
- alignment.props.left_padding = style.STANDARD_ICON_SIZE
- alignment.props.right_padding = style.GRID_CELL_SIZE
- alignment.add(grid)
- alignment.show()
- self.pack_start(alignment)
-
- return grid
-
-
-class _IgnoreSection(gtk.VBox):
+ def get_value_for_gconf(self):
+ return self.props.text
- def __init__(self):
- gtk.VBox.__init__(self)
+ def set_value_from_gconf(self, value):
+ self.props.text = value
- entry = gtk.Entry()
- entry.show()
- self.pack_start(entry, expand=False)
- _register_list_key('/system/http_proxy/ignore_hosts', entry)
+class GConfIntegerSpinButton(gtk.SpinButton, GConfMixin):
+ """Integer SpinButton backed by GConf
-class _AuthWidget(gtk.VBox):
+ It is the callers responsibility to call GConfClient.add_dir() for the
+ GConf directory containing the key.
+ """
- def __init__(self):
- gtk.VBox.__init__(self)
+ def __init__(self, gconf_key, adjustment, climb_rate=0):
+ gtk.SpinButton.__init__(self, adjustment, climb_rate=climb_rate)
+ GConfMixin.__init__(self, gconf_key)
- enable = gtk.CheckButton()
- enable.props.label = _('Use authentication')
- enable.show()
- self.pack_start(enable, expand=False)
- _register_bool_key('/system/http_proxy/use_authentication', enable)
+ def get_value_for_gconf(self):
+ return self.get_value_as_int()
- grid = _Grid(2, 2, False)
- grid.props.column_spacing = style.DEFAULT_SPACING
- grid.props.row_spacing = style.DEFAULT_SPACING
- self.pack_start(grid)
+ def set_value_from_gconf(self, value):
+ self.set_value(value)
- grid.attach_label(_('Username:'), 0, 1, 0, 1)
- entry = grid.attach_entry(1, 2, 0, 1)
- _register_string_key('/system/http_proxy/authentication_user', entry)
- grid.attach_label(_('Password:'), 0, 1, 1, 2)
- entry = grid.attach_entry(1, 2, 1, 2)
- entry.props.visibility = False
- _register_string_key(
- '/system/http_proxy/authentication_password', entry)
+class GConfStringListEntry(GConfEntry):
+ """Text entry backed by a GConf list of strings"""
- enable.connect('toggled', lambda sender:
- grid.show() if sender.props.active else grid.hide())
- if enable.props.active:
- grid.show()
+ def __init__(self, gconf_key, separator=','):
+ self._separator = separator
+ GConfEntry.__init__(self, gconf_key)
+ def get_value_for_gconf(self):
+ entries = self.props.text.split(self._separator)
+ return [entry for entry in entries if entry]
-class _Grid(gtk.Table):
+ def set_value_from_gconf(self, value):
+ self.props.text = self._separator.join(value)
- def attach_label(self, label, left_attach, right_attach,
- top_attach, bottom_attach):
- widget = gtk.Label(label)
- widget.set_alignment(0, 0)
- widget.set_line_wrap(True)
- self.attach(widget, left_attach, right_attach,
- top_attach, bottom_attach, gtk.SHRINK | gtk.FILL, gtk.SHRINK)
- widget.show()
- return widget
- def attach_entry(self, left_attach, right_attach,
- top_attach, bottom_attach):
- widget = gtk.Entry()
- self.attach(widget, left_attach, right_attach,
- top_attach, bottom_attach, gtk.EXPAND | gtk.FILL, gtk.SHRINK)
- widget.show()
- return widget
+class SettingBox(gtk.HBox):
+ """
+ Base class for "lines" on the screen representing configuration settings
+ """
+ def __init__(self, name, size_group=None):
+ gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING)
+ self.label = gtk.Label(name)
+ self.label.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ self.label.set_alignment(1, 0.5)
+ self.label.show()
+ self.pack_start(self.label, expand=False)
-def _set_sensitive(sender, reverse, widgets):
- is_sensitive = sender.props.active
- if reverse:
- is_sensitive = not is_sensitive
+ if size_group is not None:
+ size_group.add_widget(self.label)
- for i in widgets:
- if isinstance(i, gtk.Container):
- _set_sensitive(sender, reverse, i.get_children())
- i.props.sensitive = is_sensitive and _widget_sensitivies.get(i, True)
+class GConfStringSettingBox(SettingBox):
+ """A configuration line for a GConf string setting"""
-def _register_bool_key(key, widget, reverse=False):
+ def __init__(self, name, gconf_key, size_group=None):
+ SettingBox.__init__(self, name, size_group=size_group)
+ self.string_entry = GConfEntry(gconf_key)
+ self.string_entry.show()
+ self.pack_start(self.string_entry, expand=True)
- def set_cb(widget, x, reverse):
- value = x.get_bool()
- if reverse:
- value = not value
- widget.props.active = value
+ def undo(self):
+ """Revert to original value if modified"""
+ self.string_entry.undo()
- def get_cb(widget, reverse):
- x = gconf.Value(gconf.VALUE_BOOL)
- value = widget.props.active
- if reverse:
- value = not value
- x.set_bool(value)
- return x
+ @property
+ def changed(self):
+ return self.string_entry.changed
- _register_key(key, widget, 'toggled', set_cb, get_cb, reverse)
+class GConfHostListSettingBox(GConfStringSettingBox):
+ """A configuration line for a host list GConf setting"""
-def _register_string_key(key, widget):
+ def __init__(self, name, gconf_key, size_group=None):
+ SettingBox.__init__(self, name, size_group=size_group)
+ self.hosts_entry = GConfStringListEntry(gconf_key)
+ self.hosts_entry.show()
+ self.pack_start(self.hosts_entry, expand=True)
- def set_cb(widget, x):
- widget.props.text = x.get_string()
+ def undo(self):
+ """Revert to original value if modified"""
+ self.hosts_entry.undo()
+
+ @property
+ def changed(self):
+ return self.hosts_entry.changed
- def get_cb(widget):
- x = gconf.Value(gconf.VALUE_STRING)
- x.set_string(widget.props.text)
- return x
- _register_key(key, widget, 'changed', set_cb, get_cb)
+class GConfHostPortSettingBox(SettingBox):
+ """A configuration line for a combined host name and port GConf setting"""
+ def __init__(self, name, host_key, port_key, size_group=None):
+ SettingBox.__init__(self, name, size_group=size_group)
+ self.host_name_entry = GConfEntry(host_key)
+ self.host_name_entry.show()
+ self.pack_start(self.host_name_entry, expand=True)
-def _register_int_key(key, widget):
+ # port number 0 means n/a
+ adjustment = gtk.Adjustment(0, 0, 65535, 1, 10)
+ self.port_spin_button = GConfIntegerSpinButton(port_key, adjustment,
+ climb_rate=0.1)
+ self.port_spin_button.show()
+ self.pack_start(self.port_spin_button, expand=False)
- def set_cb(widget, x):
- widget.props.value = x.get_int()
+ def undo(self):
+ """Revert to original values if modified"""
+ self.host_name_entry.undo()
+ self.port_spin_button.undo()
- def get_cb(widget):
- x = gconf.Value(gconf.VALUE_INT)
- x.set_int(int(widget.props.value))
- return x
+ @property
+ def changed(self):
+ return self.host_name_entry.changed or self.port_spin_button.changed
- _register_key(key, widget.props.adjustment, 'value_changed',
- set_cb, get_cb)
+class ExclusiveOptionSetsBox(gtk.VBox):
+ """
+ Container for sets of different settings selected by a top-level setting
-def _register_selector_key(key, widget, value):
+ Renders the top level setting as a ComboBox. Only the currently
+ active set is shown on screen.
+ """
- def set_cb(widget, x, value):
- widget.props.active = x.get_string() == value
+ def __init__(self, top_name, option_sets, size_group=None):
+ """Initialize an ExclusiveOptionSetsBox instance
+
+ Arguments:
+
+ top_name -- text label used for the top-level selection
+ option_sets -- list of tuples containing text label and GTK
+ widget to display for each of the option sets
+ size_group -- optional gtk.SizeGroup to use for the top-level label
+ """
+ gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
+ self.label_size_group = size_group
+ top_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ top_box.show()
+ top_label = gtk.Label(top_name)
+ top_label.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ top_label.set_alignment(1, 0.5)
+ top_label.show()
+ self.label_size_group.add_widget(top_label)
+ top_box.pack_start(top_label, expand=False)
+
+ model = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_OBJECT)
+ self._top_combo_box = gtk.ComboBox(model=model)
+ self._top_combo_box.connect('changed', self.__combo_changed_cb)
+ self._top_combo_box.show()
+
+ cell_renderer = gtk.CellRendererText()
+ cell_renderer.props.ellipsize = pango.ELLIPSIZE_MIDDLE
+ cell_renderer.props.ellipsize_set = True
+ self._top_combo_box.pack_start(cell_renderer)
+ self._top_combo_box.add_attribute(cell_renderer, 'text', 0)
+ top_box.pack_start(self._top_combo_box, expand=True)
+ self.pack_start(top_box, expand=False)
+
+ self._settings_box = gtk.VBox()
+ self._settings_box.show()
+ self.pack_start(self._settings_box, expand=False)
+
+ for name, box in option_sets:
+ model.append((name, box))
+
+ def __combo_changed_cb(self, combobox):
+ giter = combobox.get_active_iter()
+ new_box = combobox.get_model().get(giter, 1)[0]
+ current_box = self._settings_box.get_children()
+ if current_box:
+ self._settings_box.remove(current_box[0])
+
+ self._settings_box.add(new_box)
+ new_box.show()
+
+
+class GConfExclusiveOptionSetsBox(ExclusiveOptionSetsBox, GConfMixin):
+ """
+ Container for sets of GConf settings based on a top-level setting
+ """
- def get_cb(widget, value):
- if not widget.props.active:
+ def __init__(self, top_name, top_gconf_key, option_sets, size_group=None):
+ """Initialize a GConfExclusiveOptionSetsBox instance
+
+ Arguments:
+
+ top_name -- text label used for the top-level selection
+ top_gconf_key -- key for the GConf entry to use for the
+ top-level selection
+ option_sets -- list of tuples containing text label, matching
+ GConf value as well as the GTK widget to display
+ for each of the option sets
+ size_group -- optional gtk.SizeGroup to use for the top-level label
+ """
+ display_sets = [(name, widget) for name, value, widget in option_sets]
+ self._top_mapping = dict([(name, value)
+ for name, value, widget in option_sets])
+ ExclusiveOptionSetsBox.__init__(self, top_name, display_sets,
+ size_group=size_group)
+ GConfMixin.__init__(self, top_gconf_key, self._top_combo_box)
+
+ def get_value_for_gconf(self):
+ giter = self._top_combo_box.get_active_iter()
+ if giter is None:
return None
- x = gconf.Value(gconf.VALUE_STRING)
- x.set_string(value)
- return x
- _register_key(key, widget, 'toggled', set_cb, get_cb, value)
+ name = self._top_combo_box.get_model().get(giter, 0)[0]
+ return self._top_mapping[name]
+
+ def set_value_from_gconf(self, value):
+ for idx, (name, widget_) in enumerate(self._top_combo_box.get_model()):
+ if self._top_mapping[name] == value:
+ self._top_combo_box.set_active(idx)
+ return
+ raise ValueError('Invalid value %r' % (value, ))
-def _register_list_key(key, widget):
- def set_cb(widget, x):
- hosts = [i.get_string() for i in x.get_list()]
- widget.props.text = ', '.join(hosts)
+class OptionalSettingsBox(gtk.VBox):
+ """
+ Container for settings (de)activated by a top-level setting
+
+ Renders the top level setting as a CheckButton. The settings are only
+ shown on screen if the top-level setting is enabled.
+ """
- def get_cb(widget):
- hosts = []
- for i in re.split('[\s,;:]+', widget.props.text or ''):
- if not i.strip():
- continue
- value = gconf.Value(gconf.VALUE_STRING)
- value.set_string(i.strip())
- hosts.append(value)
- x = gconf.Value(gconf.VALUE_LIST)
- x.set_list_type(gconf.VALUE_STRING)
- x.set_list(hosts)
- return x
+ def __init__(self, top_name, options):
+ """Initialize an OptionalSettingsBox instance
- _register_key(key, widget, 'changed', set_cb, get_cb)
+ Arguments:
+ top_name -- text label used for the top-level selection
+ options -- list of GTK widgets to display for each of the options
+ """
+ gtk.VBox.__init__(self, spacing=style.DEFAULT_SPACING)
+ self._top_check_button = gtk.CheckButton()
+ self._top_check_button.props.label = top_name
+ self._top_check_button.connect('toggled', self.__button_changed_cb)
+ self._top_check_button.show()
+ self.pack_start(self._top_check_button, expand=True)
-def _register_key(key, widget, signal, set_cb, get_cb, *args):
- conf = gconf.client_get_default()
- value = conf.get(key)
- if value is not None:
- set_cb(widget, value, *args)
+ self._settings_box = gtk.VBox(spacing=style.DEFAULT_SPACING)
+ self.pack_start(self._settings_box, expand=False)
- _gconf_origin_values[key] = value
+ for box in options:
+ self._settings_box.pack_start(box)
+
+ def __button_changed_cb(self, check_button):
+ if check_button.get_active():
+ self._settings_box.show()
+ else:
+ self._settings_box.hide()
+
+
+class GConfOptionalSettingsBox(OptionalSettingsBox, GConfMixin):
+ """
+ Container for GConf settings (de)activated by a top-level setting
+ """
- def signal_cb(sender, key, widget, get_cb, *args):
- value = get_cb(widget, *args)
- if value is not None:
- conf = gconf.client_get_default()
- conf.set(key, value)
+ def __init__(self, top_name, top_gconf_key, options):
+ """Initialize a GConfExclusiveOptionSetsBox instance
- widget.connect(signal, signal_cb, key, widget, get_cb, *args)
-#################################################################################################################
-# "Proxy" code-merging finished !!
+ Arguments:
+ top_name -- text label used for the top-level selection
+ top_gconf_key -- key for the GConf entry to use for the
+ top-level selection
+ options -- list of GTK widgets to display for each of the options
+ """
+ OptionalSettingsBox.__init__(self, top_name, options)
+ GConfMixin.__init__(self, top_gconf_key, self._top_check_button,
+ signal='toggled')
+ def get_value_for_gconf(self):
+ return self._top_check_button.get_active()
+
+ def set_value_from_gconf(self, value):
+ self._top_check_button.set_active(value)
class AddRemoveWidget(gtk.HBox):
@@ -577,6 +574,11 @@ class Network(SectionView):
self._jabber_change_handler = None
self._radio_change_handler = None
self._network_configuration_reset_handler = None
+ self._undo_objects = []
+
+ client = gconf.client_get_default()
+ client.add_dir('/system/http_proxy', gconf.CLIENT_PRELOAD_ONELEVEL)
+ client.add_dir('/system/proxy', gconf.CLIENT_PRELOAD_ONELEVEL)
self.set_border_width(style.DEFAULT_SPACING * 2)
self.set_spacing(style.DEFAULT_SPACING)
@@ -703,6 +705,12 @@ class Network(SectionView):
workspace.pack_start(box_mesh, expand=False)
box_mesh.show()
+ proxy_separator = gtk.HSeparator()
+ workspace.pack_start(proxy_separator, False)
+ proxy_separator.show()
+
+ self._add_proxy_section(workspace)
+
if self._model.is_hidden_network_connect_package_available():
separator_hidden_network = gtk.HSeparator()
workspace.pack_start(separator_hidden_network, False)
@@ -752,30 +760,6 @@ class Network(SectionView):
box_hidden_network.show()
- separator_proxy = gtk.HSeparator()
- workspace.pack_start(separator_proxy, False)
- separator_proxy.show()
-
- label_proxy = gtk.Label(_('Proxy'))
- label_proxy.set_alignment(0, 0)
- workspace.pack_start(label_proxy, expand=False)
- label_proxy.show()
-
- box_proxy = gtk.VBox()
- box_proxy.set_border_width(style.DEFAULT_SPACING * 2)
- box_proxy.set_spacing(style.DEFAULT_SPACING)
- proxy_info = gtk.Label(_("<Enter suitable info>"))
- proxy_info.set_alignment(0, 0)
- proxy_info.set_line_wrap(True)
- box_proxy.pack_start(proxy_info, expand=False)
- proxy_info.show()
- workspace.pack_start(box_proxy, expand=False)
- box_proxy.show()
-
- proxy = Proxy()
- workspace.pack_start(proxy, expand=False)
- proxy.show()
-
separator_nm_connection_editor = gtk.HSeparator()
workspace.pack_start(separator_nm_connection_editor, False)
separator_nm_connection_editor.show()
@@ -817,6 +801,107 @@ class Network(SectionView):
self.setup()
+ def _add_proxy_section(self, workspace):
+ proxy_title = gtk.Label(_('Proxy'))
+ proxy_title.set_alignment(0, 0)
+ proxy_title.show()
+ workspace.pack_start(proxy_title, expand=False)
+
+ proxy_box = gtk.VBox()
+ proxy_box.set_border_width(style.DEFAULT_SPACING * 2)
+ proxy_box.set_spacing(style.DEFAULT_SPACING)
+ proxy_box.show()
+
+ workspace.pack_start(proxy_box)
+
+ size_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ automatic_proxy_box = gtk.VBox()
+ automatic_proxy_box.set_spacing(style.DEFAULT_SPACING)
+
+ url_box = GConfStringSettingBox(_('Configuration URL:'),
+ '/system/proxy/autoconfig_url',
+ size_group)
+ url_box.show()
+ automatic_proxy_box.pack_start(url_box)
+ self._undo_objects.append(url_box)
+
+ wpad_help_text = _('Web Proxy Autodiscovery (WPAD) is used when a'
+ ' Configuration URL is not provided. This is not'
+ ' recommended for untrusted public networks.')
+ automatic_proxy_help = WrappedLabel(wpad_help_text)
+ automatic_proxy_help.set_alignment(0, 0)
+ automatic_proxy_help.show()
+ automatic_proxy_box.pack_start(automatic_proxy_help)
+
+ manual_proxy_box = gtk.VBox()
+ manual_proxy_box.set_spacing(style.DEFAULT_SPACING)
+
+ http_box = GConfHostPortSettingBox(_('HTTP Proxy:'),
+ '/system/http_proxy/host',
+ '/system/http_proxy/port',
+ size_group)
+ http_box.show()
+ manual_proxy_box.pack_start(http_box)
+ self._undo_objects.append(http_box)
+
+ user_name_box = GConfStringSettingBox(_('Username:'),
+ '/system/http_proxy/authentication_user', size_group)
+ user_name_box.show()
+ self._undo_objects.append(user_name_box)
+
+ password_box = GConfStringSettingBox(_('Password:'),
+ '/system/http_proxy/authentication_password', size_group)
+ password_box.show()
+ self._undo_objects.append(password_box)
+
+ auth_box = GConfOptionalSettingsBox(_('Use authentication'),
+ '/system/http_proxy/use_authentication',
+ [user_name_box, password_box])
+ auth_box.show()
+ manual_proxy_box.pack_start(auth_box)
+ self._undo_objects.append(auth_box)
+
+ https_box = GConfHostPortSettingBox(_('HTTPS Proxy:'),
+ '/system/proxy/secure_host',
+ '/system/proxy/secure_port',
+ size_group)
+ https_box.show()
+ manual_proxy_box.pack_start(https_box)
+ self._undo_objects.append(https_box)
+
+ ftp_box = GConfHostPortSettingBox(_('FTP Proxy:'),
+ '/system/proxy/ftp_host',
+ '/system/proxy/ftp_port',
+ size_group)
+ ftp_box.show()
+ manual_proxy_box.pack_start(ftp_box)
+ self._undo_objects.append(ftp_box)
+
+ socks_box = GConfHostPortSettingBox(_('SOCKS Proxy:'),
+ '/system/proxy/socks_host',
+ '/system/proxy/socks_port',
+ size_group)
+ socks_box.show()
+ manual_proxy_box.pack_start(socks_box)
+ self._undo_objects.append(socks_box)
+
+ option_sets = [('None', 'none', gtk.VBox()),
+ ('Automatic', 'auto', automatic_proxy_box),
+ ('Manual', 'manual', manual_proxy_box)]
+ option_sets_box = GConfExclusiveOptionSetsBox(_('Method:'),
+ '/system/proxy/mode',
+ option_sets, size_group)
+ option_sets_box.show()
+ proxy_box.pack_start(option_sets_box, expand=False)
+ self._undo_objects.append(option_sets_box)
+
+ no_proxy_box = GConfHostListSettingBox(_('Ignored Hosts'),
+ '/system/http_proxy/ignore_hosts', size_group)
+ no_proxy_box.show()
+ proxy_box.pack_start(no_proxy_box, expand=False)
+ self._undo_objects.append(no_proxy_box)
+
def setup(self):
self._entry.set_text(self._model.get_jabber())
try:
@@ -829,7 +914,6 @@ class Network(SectionView):
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( \
@@ -853,6 +937,29 @@ class Network(SectionView):
self._model.undo()
self._jabber_alert.hide()
self._radio_alert.hide()
+ for setting in self._undo_objects:
+ setting.undo()
+
+ # pylint: disable=E0202
+ @property
+ def needs_restart(self):
+ # Some parts of Sugar as well as many non-Gnome applications
+ # use environment variables rather than gconf for proxy
+ # settings, so we need to restart for the changes to take
+ # _full_ effect.
+ for setting in self._undo_objects:
+ if setting.changed:
+ return True
+
+ return False
+
+ # pylint: disable=E0102,E1101
+ @needs_restart.setter
+ def needs_restart(self, value):
+ # needs_restart is a property (i.e. gets calculated) in this Control
+ # Panel, but SectionView.__init__() wants to initialise it to False,
+ # so we need to provide a (fake) setter.
+ pass
def _validate(self):
if self._jabber_valid and self._radio_valid:
@@ -897,6 +1004,9 @@ class Network(SectionView):
self._jabber_valid = True
self._jabber_alert.hide()
+ for setting in self._undo_objects:
+ setting.undo()
+
self._validate()
return False
@@ -913,3 +1023,20 @@ class Network(SectionView):
def __launch_button_clicked_cb(self, launch_button):
self._model.launch_nm_connection_editor()
+
+
+def _gconf_value_to_python(gconf_value):
+ if gconf_value.type == gconf.VALUE_STRING:
+ return gconf_value.get_string()
+ elif gconf_value.type == gconf.VALUE_INT:
+ return gconf_value.get_int()
+ elif gconf_value.type == gconf.VALUE_FLOAT:
+ return gconf_value.get_float()
+ elif gconf_value.type == gconf.VALUE_BOOL:
+ return gconf_value.get_bool()
+ elif gconf_value.type == gconf.VALUE_LIST:
+ return [_gconf_value_to_python(entry)
+ for entry in gconf_value.get_list()]
+ else:
+ raise TypeError("Don't know how to handle GConf value"
+ " type %r" % (gconf_value.type, ))