From 755d4c888a996649d44566250337f60fcbcbcfab Mon Sep 17 00:00:00 2001 From: Andrés Ambrois Date: Mon, 30 Aug 2010 19:21:24 +0000 Subject: Merge branch '3g-db' --- diff --git a/configure.ac b/configure.ac index b4cc189..edc275e 100644 --- a/configure.ac +++ b/configure.ac @@ -56,6 +56,7 @@ extensions/cpsection/frame/Makefile extensions/cpsection/keyboard/Makefile extensions/cpsection/language/Makefile extensions/cpsection/modemconfiguration/Makefile +extensions/cpsection/modemconfiguration/config.py extensions/cpsection/Makefile extensions/cpsection/network/Makefile extensions/cpsection/power/Makefile diff --git a/extensions/cpsection/modemconfiguration/Makefile.am b/extensions/cpsection/modemconfiguration/Makefile.am index 3e2613e..525e02e 100644 --- a/extensions/cpsection/modemconfiguration/Makefile.am +++ b/extensions/cpsection/modemconfiguration/Makefile.am @@ -4,3 +4,5 @@ sugar_PYTHON = \ __init__.py \ model.py \ view.py + +nodist_sugar_PYTHON = config.py diff --git a/extensions/cpsection/modemconfiguration/config.py.in b/extensions/cpsection/modemconfiguration/config.py.in new file mode 100644 index 0000000..6fa688e --- /dev/null +++ b/extensions/cpsection/modemconfiguration/config.py.in @@ -0,0 +1,20 @@ +# -*- encoding: utf-8 -*- +# Copyright (C) 2010 Andrés Ambrois +# +# 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 2 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, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US + +PROVIDERS_PATH = "@prefix@/share/mobile-broadband-provider-info/serviceproviders.xml" +PROVIDERS_FORMAT_SUPPORTED = "2.0" +COUNTRY_CODES_PATH = "@prefix@/share/zoneinfo/iso3166.tab" diff --git a/extensions/cpsection/modemconfiguration/model.py b/extensions/cpsection/modemconfiguration/model.py index 2545ce1..73b9c63 100755..100644 --- a/extensions/cpsection/modemconfiguration/model.py +++ b/extensions/cpsection/modemconfiguration/model.py @@ -14,12 +14,38 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US +from __future__ import with_statement import gconf +import gtk +import os +import locale +import logging + +from xml.etree.cElementTree import ElementTree +from gettext import gettext as _ from jarabe.model.network import GSM_USERNAME_PATH, GSM_PASSWORD_PATH, \ GSM_NUMBER_PATH, GSM_APN_PATH, GSM_PIN_PATH, \ GSM_PUK_PATH +from cpsection.modemconfiguration.config import PROVIDERS_PATH, \ + PROVIDERS_FORMAT_SUPPORTED, \ + COUNTRY_CODES_PATH + +_country_code, _encoding = locale.getdefaultlocale() +if _country_code is not None: + _tags = _country_code.lower().split('_') + LANG = _tags[0] + if len(_tags) > 1 and len(_tags[1]) == 2: + COUNTRY_CODE = _tags[1] + else: + COUNTRY_CODE = None +else: + LANG = None + COUNTRY_CODE = None + +LANG_NS_ATTR = '{http://www.w3.org/XML/1998/namespace}lang' + def get_username(): client = gconf.client_get_default() return client.get_string(GSM_USERNAME_PATH) or '' @@ -68,3 +94,114 @@ def set_puk(puk): client = gconf.client_get_default() client.set_string(GSM_PUK_PATH, puk) +def has_providers_db(): + if not os.path.isfile(COUNTRY_CODES_PATH): + logging.debug("Mobile broadband provider database: Country " \ + "codes path %s not found.", COUNTRY_CODES_PATH) + return False + try: + tree = ElementTree(file=PROVIDERS_PATH) + except (IOError, SyntaxError), e: + logging.debug("Mobile broadband provider database: Could not read " \ + "provider information %s error=%s", PROVIDERS_PATH) + return False + else: + elem = tree.getroot() + if elem is None or elem.get('format') != PROVIDERS_FORMAT_SUPPORTED: + logging.debug("Mobile broadband provider database: Could not " \ + "read provider information. %s is wrong format.", + elem.get('format')) + return False + return True + + +class CountryListStore(gtk.ListStore): + def __init__(self): + gtk.ListStore.__init__(self, str, object) + codes = {} + with open(COUNTRY_CODES_PATH) as codes_file: + for line in codes_file: + if line.startswith('#'): + continue + code, name = line.split('\t')[:2] + codes[code.lower()] = name.strip() + etree = ElementTree(file=PROVIDERS_PATH).getroot() + self._country_idx = None + i = 0 + for elem in etree.findall('.//country'): + code = elem.attrib['code'] + if code == COUNTRY_CODE: + self._country_idx = i + else: + i += 1 + if code in codes: + self.append((codes[code], elem)) + else: + self.append((code, elem)) + + def get_row_providers(self, row): + return self[row][1] + + def guess_country_row(self): + if self._country_idx is not None: + return self._country_idx + else: + return -1 + +class ProviderListStore(gtk.ListStore): + def __init__(self, elem): + gtk.ListStore.__init__(self, str, object) + for provider_elem in elem.findall('.//provider'): + apns = provider_elem.findall('.//apn') + if not apns: + # Skip carriers with CDMA entries only + continue + names = provider_elem.findall('.//name') + for name in names: + if name.get(LANG_NS_ATTR) is None: + # serviceproviders.xml default value + provider_name = name.text + elif name.get(LANG_NS_ATTR) == LANG: + # Great! We found a name value for our locale! + provider_name = name.text + break + self.append((provider_name, apns)) + + def get_row_plans(self, row): + return self[row][1] + +class PlanListStore(gtk.ListStore): + DEFAULT_NUMBER = '*99#' + + def __init__(self, elems): + gtk.ListStore.__init__(self, str, object) + for apn_elem in elems: + plan = {} + names = apn_elem.findall('.//name') + if names: + for name in names: + if name.get(LANG_NS_ATTR) is None: + plan['name'] = name.text + elif name.get(LANG_NS_ATTR) == LANG: + plan['name'] = name.text + break + else: + plan['name'] = _('Default') + plan['apn'] = apn_elem.get('value') + user = apn_elem.find('.//username') + if user is not None: + plan['username'] = user.text + else: + plan['username'] = '' + passwd = apn_elem.find('.//password') + if passwd is not None: + plan['password'] = passwd.text + else: + plan['password'] = '' + + plan['number'] = self.DEFAULT_NUMBER + + self.append((plan['name'], plan)) + + def get_row_plan(self, row): + return self[row][1] diff --git a/extensions/cpsection/modemconfiguration/view.py b/extensions/cpsection/modemconfiguration/view.py index b236f3f..c6850d3 100644 --- a/extensions/cpsection/modemconfiguration/view.py +++ b/extensions/cpsection/modemconfiguration/view.py @@ -14,8 +14,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US -import os -import logging from gettext import gettext as _ import gtk @@ -31,7 +29,7 @@ class EntryWithLabel(gtk.HBox): __gtype_name__ = "SugarEntryWithLabel" def __init__(self, label_text): - gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING) + gtk.HBox.__init__(self, spacing=style.DEFAULT_SPACING * 2) self._timeout_sid = 0 self._changed_handler = None @@ -44,11 +42,11 @@ class EntryWithLabel(gtk.HBox): self.pack_start(self.label, expand=False) self.label.show() - self._entry = gtk.Entry(25) - self._entry.connect('changed', self.__entry_changed_cb) - self._entry.set_width_chars(25) - self.pack_start(self._entry, expand=False) - self._entry.show() + self.entry = gtk.Entry(25) + self.entry.connect('changed', self.__entry_changed_cb) + self.entry.set_width_chars(25) + self.pack_start(self.entry, expand=False) + self.entry.show() def __entry_changed_cb(self, widget, data=None): if self._timeout_sid: @@ -59,11 +57,11 @@ class EntryWithLabel(gtk.HBox): def __timeout_cb(self): self._timeout_sid = 0 - if self._entry.get_text() == self.get_value(): + if self.entry.get_text() == self.get_value(): return False try: - self.set_value(self._entry.get_text()) + self.set_value(self.entry.get_text()) except ValueError: self._is_valid = False else: @@ -74,18 +72,20 @@ class EntryWithLabel(gtk.HBox): return False def set_text_from_model(self): - self._entry.set_text(self.get_value()) + self.entry.set_text(self.get_value()) def get_value(self): raise NotImplementedError - def set_value(self): + def set_value(self, value): raise NotImplementedError def _get_is_valid(self): return self._is_valid + is_valid = gobject.property(type=bool, getter=_get_is_valid, default=True) + class UsernameEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Username:')) @@ -97,6 +97,7 @@ class UsernameEntry(EntryWithLabel): def set_value(self, username): self._model.set_username(username) + class PasswordEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Password:')) @@ -108,6 +109,7 @@ class PasswordEntry(EntryWithLabel): def set_value(self, password): self._model.set_password(password) + class NumberEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Number:')) @@ -119,6 +121,7 @@ class NumberEntry(EntryWithLabel): def set_value(self, number): self._model.set_number(number) + class ApnEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Access Point Name (APN):')) @@ -130,6 +133,7 @@ class ApnEntry(EntryWithLabel): def set_value(self, apn): self._model.set_apn(apn) + class PinEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Personal Identity Number (PIN):')) @@ -141,6 +145,7 @@ class PinEntry(EntryWithLabel): def set_value(self, pin): self._model.set_pin(pin) + class PukEntry(EntryWithLabel): def __init__(self, model): EntryWithLabel.__init__(self, _('Personal Unblocking Key (PUK):')) @@ -160,65 +165,137 @@ class ModemConfiguration(SectionView): self._model = model self.restart_alerts = alerts - self.set_border_width(style.DEFAULT_SPACING) self.set_spacing(style.DEFAULT_SPACING) - self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + + label_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + combo_group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + + scrolled_win = gtk.ScrolledWindow() + scrolled_win.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + scrolled_win.show() + self.add(scrolled_win) + + main_box = gtk.VBox(spacing=style.DEFAULT_SPACING) + main_box.set_border_width(style.DEFAULT_SPACING) + main_box.show() + scrolled_win.add_with_viewport(main_box) explanation = _("You will need to provide the following " \ "information to set up a mobile " \ "broadband connection to a cellular "\ "(3G) network.") 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) + main_box.pack_start(self._text, False) self._text.show() + if model.has_providers_db(): + self.__add_provider_information(model, main_box, label_group, + combo_group) + + self._lower_box = gtk.VBox(spacing=style.DEFAULT_SPACING) + self._lower_box.set_border_width(style.DEFAULT_SPACING) + main_box.pack_start(self._lower_box, expand=False) + self._lower_box.show() + self._username_entry = UsernameEntry(model) self._username_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._username_entry.label) - self.pack_start(self._username_entry, expand=False) + label_group.add_widget(self._username_entry.label) + combo_group.add_widget(self._username_entry.entry) + self._lower_box.pack_start(self._username_entry, fill=False) self._username_entry.show() self._password_entry = PasswordEntry(model) self._password_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._password_entry.label) - self.pack_start(self._password_entry, expand=False) + label_group.add_widget(self._password_entry.label) + combo_group.add_widget(self._password_entry.entry) + self._lower_box.pack_start(self._password_entry, fill=False) self._password_entry.show() self._number_entry = NumberEntry(model) self._number_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._number_entry.label) - self.pack_start(self._number_entry, expand=False) + label_group.add_widget(self._number_entry.label) + combo_group.add_widget(self._number_entry.entry) + self._lower_box.pack_start(self._number_entry, fill=False) self._number_entry.show() self._apn_entry = ApnEntry(model) self._apn_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._apn_entry.label) - self.pack_start(self._apn_entry, expand=False) + label_group.add_widget(self._apn_entry.label) + combo_group.add_widget(self._apn_entry.entry) + self._lower_box.pack_start(self._apn_entry, fill=False) self._apn_entry.show() self._pin_entry = PinEntry(model) self._pin_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._pin_entry.label) - self.pack_start(self._pin_entry, expand=False) + label_group.add_widget(self._pin_entry.label) + self._lower_box.pack_start(self._pin_entry, fill=False) self._pin_entry.show() self._puk_entry = PukEntry(model) self._puk_entry.connect('notify::is-valid', self.__notify_is_valid_cb) - self._group.add_widget(self._puk_entry.label) - self.pack_start(self._puk_entry, expand=False) + label_group.add_widget(self._puk_entry.label) + combo_group.add_widget(self._puk_entry.entry) + self._lower_box.pack_start(self._puk_entry, fill=False) self._puk_entry.show() self.setup() + def __add_provider_combo(self, parent_box, text, label_group, store, cb): + box = gtk.HBox(spacing=style.DEFAULT_SPACING * 2) + label = gtk.Label(text) + label.set_alignment(1, 0.5) + label_group.add_widget(label) + box.pack_start(label, False) + label.show() + + combo = gtk.ComboBox(store) + cell = gtk.CellRendererText() + cell.props.xalign = 0.5 + combo.pack_start(cell) + combo.add_attribute(cell, 'text', 0) + combo.connect('changed', cb) + + box.pack_start(combo, False) + combo.show() + parent_box.pack_start(box, False) + box.show() + + return combo + + def __add_provider_information(self, model, main_box, label_group, + combo_group): + upper_box = gtk.VBox(spacing=style.DEFAULT_SPACING) + upper_box.set_border_width(style.DEFAULT_SPACING) + main_box.pack_start(upper_box, expand=False) + upper_box.show() + + country_store = model.CountryListStore() + self._country_combo = self.__add_provider_combo(upper_box, _('Country:'), + label_group, country_store, self.__country_selected_cb) + combo_group.add_widget(self._country_combo) + + self._providers_combo = self.__add_provider_combo(upper_box, + _('Provider:'), label_group, None, self.__provider_selected_cb) + combo_group.add_widget(self._providers_combo) + + self._plan_combo = self.__add_provider_combo(upper_box, _('Plan:'), label_group, None, + self.__plan_selected_cb) + combo_group.add_widget(self._plan_combo) + + self._country_combo.set_active(country_store.guess_country_row()) + + separator = gtk.HSeparator() + main_box.pack_start(separator, False) + separator.show() + def setup(self): self._username_entry.set_text_from_model() self._password_entry.set_text_from_model() @@ -232,14 +309,37 @@ class ModemConfiguration(SectionView): def undo(self): self._model.undo() + def __country_selected_cb(self, combo): + model = combo.get_model() + providers = model.get_row_providers(combo.get_active()) + self._providers_combo.set_model( + self._model.ProviderListStore(providers)) + + def __provider_selected_cb(self, combo): + model = combo.get_model() + plans = model.get_row_plans(combo.get_active()) + self._plan_combo.set_model(self._model.PlanListStore(plans)) + + def __plan_selected_cb(self, combo): + model = combo.get_model() + plan = model.get_row_plan(combo.get_active()) + self._username_entry.set_value(plan['username']) + self._username_entry.set_text_from_model() + self._password_entry.set_value(plan['password']) + self._password_entry.set_text_from_model() + self._number_entry.set_value(plan['number']) + self._number_entry.set_text_from_model() + self._apn_entry.set_value(plan['apn']) + self._apn_entry.set_text_from_model() + def _validate(self): if self._username_entry.is_valid and \ - self._password_entry.is_valid and \ + self._password_entry.is_valid and \ self._number_entry.is_valid and \ - self._apn_entry.is_valid and \ - self._pin_entry.is_valid and \ - self._puk_entry.is_valid: - self.props.is_valid = True + self._apn_entry.is_valid and \ + self._pin_entry.is_valid and \ + self._puk_entry.is_valid: + self.props.is_valid = True else: self.props.is_valid = False @@ -247,4 +347,3 @@ class ModemConfiguration(SectionView): if entry.is_valid: self.needs_restart = True self._validate() - -- cgit v0.9.1