[Dextrose] [PATCH] Database support for 3G control panel
Martin Abente
martin.abente.lahaye at gmail.com
Tue Jan 11 09:52:52 EST 2011
From: Andrés Ambrois <andresambrois at gmail.com>
For more information please look at #1630
---
configure.ac | 1 +
.../cpsection/modemconfiguration/Makefile.am | 2 +
.../cpsection/modemconfiguration/config.py.in | 20 ++
extensions/cpsection/modemconfiguration/model.py | 119 +++++++++++++
extensions/cpsection/modemconfiguration/view.py | 184 ++++++++++++++++----
5 files changed, 292 insertions(+), 34 deletions(-)
create mode 100644 extensions/cpsection/modemconfiguration/config.py.in
mode change 100755 => 100644 extensions/cpsection/modemconfiguration/model.py
diff --git a/configure.ac b/configure.ac
index 13a2f09..68a8bf3 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
old mode 100755
new mode 100644
index 2545ce1..dbec47d
--- a/extensions/cpsection/modemconfiguration/model.py
+++ b/extensions/cpsection/modemconfiguration/model.py
@@ -15,11 +15,22 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 US
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
+
def get_username():
client = gconf.client_get_default()
return client.get_string(GSM_USERNAME_PATH) or ''
@@ -68,3 +79,111 @@ 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):
+ COUNTRY_CODE = locale.getdefaultlocale()[0][3:5].lower()
+
+ 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 == self.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
+ self.append((provider_elem.find('.//name').text, apns))
+
+ def get_row_plans(self, row):
+ return self[row][1]
+
+class PlanListStore(gtk.ListStore):
+ LANG_NS_ATTR = '{http://www.w3.org/XML/1998/namespace}lang'
+ LANG = locale.getdefaultlocale()[0][:2]
+ 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(self.LANG_NS_ATTR) is None:
+ # serviceproviders.xml default value
+ plan['name'] = name.text
+ elif name.get(self.LANG_NS_ATTR) == self.LANG:
+ # Great! We found a name value for our locale!
+ 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..3b03b3d 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,61 +165,150 @@ 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._upper_box = gtk.VBox(spacing=style.DEFAULT_SPACING)
+ self._upper_box.set_border_width(style.DEFAULT_SPACING)
+ main_box.pack_start(self._upper_box, expand=False)
+ self._upper_box.show()
+
+
+ box = gtk.HBox(spacing=style.DEFAULT_SPACING * 2)
+ label = gtk.Label(_('Country:'))
+ label.set_alignment(1, 0.5)
+ label_group.add_widget(label)
+ box.pack_start(label, False)
+ label.show()
+ country_store = model.CountryListStore()
+ country_combo = gtk.ComboBox(country_store)
+ combo_group.add_widget(country_combo)
+ cell = gtk.CellRendererText()
+ cell.props.xalign = 0.5
+ country_combo.pack_start(cell)
+ country_combo.add_attribute(cell, 'text', 0)
+ country_combo.connect('changed', self.__country_selected_cb)
+ box.pack_start(country_combo, False)
+ country_combo.show()
+ self._upper_box.pack_start(box, False)
+ box.show()
+
+ box = gtk.HBox(spacing=style.DEFAULT_SPACING * 2)
+ label = gtk.Label(_('Provider:'))
+ label.set_alignment(1, 0.5)
+ label_group.add_widget(label)
+ box.pack_start(label, False)
+ label.show()
+ self._providers_combo = gtk.ComboBox()
+ combo_group.add_widget(self._providers_combo)
+ cell = gtk.CellRendererText()
+ cell.props.xalign = 0.5
+ self._providers_combo.pack_start(cell)
+ self._providers_combo.add_attribute(cell, 'text', 0)
+ self._providers_combo.connect('changed',
+ self.__provider_selected_cb)
+ box.pack_start(self._providers_combo, False)
+ self._providers_combo.show()
+ self._upper_box.pack_start(box, False)
+ box.show()
+
+ box = gtk.HBox(spacing=style.DEFAULT_SPACING*2)
+ label = gtk.Label(_('Plan:'))
+ label.set_alignment(1, 0.5)
+ label_group.add_widget(label)
+ box.pack_start(label, False)
+ label.show()
+ self._plan_combo = gtk.ComboBox()
+ combo_group.add_widget(self._plan_combo)
+ cell = gtk.CellRendererText()
+ cell.props.xalign = 0.5
+ self._plan_combo.pack_start(cell)
+ self._plan_combo.add_attribute(cell, 'text', 0)
+ self._plan_combo.connect('changed', self.__plan_selected_cb)
+ box.pack_start(self._plan_combo, False)
+ self._plan_combo.show()
+ self._upper_box.pack_start(box, False)
+ box.show()
+
+ country_combo.set_active(country_store.guess_country_row())
+
+ separator = gtk.HSeparator()
+ main_box.pack_start(separator, False)
+ separator.show()
+
+ 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()
@@ -232,14 +326,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 +364,3 @@ class ModemConfiguration(SectionView):
if entry.is_valid:
self.needs_restart = True
self._validate()
-
--
1.7.1
More information about the Dextrose
mailing list