Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--configure.ac2
-rw-r--r--src/controlpanel/Makefile.am8
-rw-r--r--src/controlpanel/cmd.py2
-rw-r--r--src/controlpanel/controltoolbar.py157
-rw-r--r--src/controlpanel/detailview.py21
-rw-r--r--src/controlpanel/gui.py359
-rw-r--r--src/controlpanel/icons/Makefile.am10
-rw-r--r--src/controlpanel/icons/module-about_me.svg7
-rw-r--r--src/controlpanel/icons/module-about_my_xo.svg6
-rw-r--r--src/controlpanel/icons/module-date_and_time.svg19
-rw-r--r--src/controlpanel/icons/module-language.svg59
-rw-r--r--src/controlpanel/icons/module-network.svg32
-rw-r--r--src/controlpanel/inlinealert.py72
-rw-r--r--src/controlpanel/view/Makefile.am9
-rw-r--r--src/controlpanel/view/__init__.py17
-rw-r--r--src/controlpanel/view/aboutme.py231
-rw-r--r--src/controlpanel/view/aboutxo.py120
-rw-r--r--src/controlpanel/view/language.py109
-rw-r--r--src/controlpanel/view/network.py212
-rw-r--r--src/controlpanel/view/timezone.py108
-rw-r--r--src/view/home/activitiesring.py68
21 files changed, 1568 insertions, 60 deletions
diff --git a/configure.ac b/configure.ac
index d6e8b12..1981fbc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,7 +43,9 @@ data/Makefile
service/Makefile
src/Makefile
src/controlpanel/Makefile
+src/controlpanel/icons/Makefile
src/controlpanel/model/Makefile
+src/controlpanel/view/Makefile
src/intro/Makefile
src/hardware/Makefile
src/view/Makefile
diff --git a/src/controlpanel/Makefile.am b/src/controlpanel/Makefile.am
index d50559d..e626af4 100644
--- a/src/controlpanel/Makefile.am
+++ b/src/controlpanel/Makefile.am
@@ -1,4 +1,10 @@
+SUBDIRS = model view icons
+
sugardir = $(pkgdatadir)/shell/controlpanel
sugar_PYTHON = \
__init__.py \
- cmd.py
+ cmd.py \
+ gui.py \
+ controltoolbar.py \
+ detailview.py \
+ inlinealert.py
diff --git a/src/controlpanel/cmd.py b/src/controlpanel/cmd.py
index da89cda..db23559 100644
--- a/src/controlpanel/cmd.py
+++ b/src/controlpanel/cmd.py
@@ -21,7 +21,7 @@ from gettext import gettext as _
def cmd_help():
- '''Print the help for to the screen'''
+ '''Print the help to the screen'''
print _('Usage: sugar-control-panel [ option ] key [ args ... ] \n\
Control for the sugar environment. \n\
Options: \n\
diff --git a/src/controlpanel/controltoolbar.py b/src/controlpanel/controltoolbar.py
new file mode 100644
index 0000000..2d54bdc
--- /dev/null
+++ b/src/controlpanel/controltoolbar.py
@@ -0,0 +1,157 @@
+# Copyright (C) 2007, 2008 One Laptop Per Child
+#
+# 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 USA
+
+import gtk
+import gettext
+import gobject
+
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics.icon import Icon
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics import iconentry
+from sugar.graphics import style
+
+class MainToolbar(gtk.Toolbar):
+ """ Main
+ """
+ __gtype_name__ = 'MainToolbar'
+
+ __gsignals__ = {
+ 'stop-clicked': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ 'search-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str]))
+ }
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+
+ self._add_separator()
+
+ tool_item = gtk.ToolItem()
+ self.insert(tool_item, -1)
+ tool_item.show()
+ self._search_entry = iconentry.IconEntry()
+ self._search_entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
+ 'system-search')
+ self._search_entry.add_clear_button()
+ self._search_entry.set_width_chars(25)
+ self._search_entry.connect('changed', self.__search_entry_changed_cb)
+ tool_item.add(self._search_entry)
+ self._search_entry.show()
+
+ self._add_separator(True)
+
+ self.stop = ToolButton(icon_name='dialog-cancel')
+ self.stop.set_tooltip(_('Done'))
+ self.stop.connect('clicked', self.__stop_clicked_cb)
+ self.stop.show()
+ self.insert(self.stop, -1)
+ self.stop.show()
+
+ def get_entry(self):
+ return self._search_entry
+
+ def _add_separator(self, expand=False):
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ if expand:
+ separator.set_expand(True)
+ else:
+ separator.set_size_request(style.DEFAULT_SPACING, -1)
+ self.insert(separator, -1)
+ separator.show()
+
+ def __search_entry_changed_cb(self, search_entry):
+ self.emit('search-changed', search_entry.props.text)
+
+ def __stop_clicked_cb(self, button):
+ self.emit('stop-clicked')
+
+class DetailToolbar(gtk.Toolbar):
+ """ Detail
+ """
+ __gtype_name__ = 'DetailToolbar'
+
+ __gsignals__ = {
+ 'cancel-clicked': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([])),
+ 'accept-clicked': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
+ }
+ def __init__(self):
+ gtk.Toolbar.__init__(self)
+
+ self._add_separator()
+
+ self._icon = Icon()
+ self._add_widget(self._icon)
+
+ self._add_separator()
+
+ self._title = gtk.Label()
+ self._add_widget(self._title)
+
+ self._add_separator(True)
+
+ cancel_button = ToolButton('dialog-cancel')
+ cancel_button.set_tooltip(_('Cancel'))
+ cancel_button.connect('clicked', self.__cancel_button_clicked_cb)
+ self.insert(cancel_button, -1)
+ cancel_button.show()
+
+ self.accept_button = ToolButton('dialog-ok')
+ self.accept_button.set_tooltip(_('Ok'))
+ self.accept_button.connect('clicked', self.__accept_button_clicked_cb)
+ self.insert(self.accept_button, -1)
+ self.accept_button.show()
+
+ def get_icon(self):
+ return self._icon
+
+ def get_title(self):
+ return self._title
+
+ def _add_separator(self, expand=False):
+ separator = gtk.SeparatorToolItem()
+ separator.props.draw = False
+ if expand:
+ separator.set_expand(True)
+ else:
+ separator.set_size_request(style.DEFAULT_SPACING, -1)
+ self.insert(separator, -1)
+ separator.show()
+
+ def _add_widget(self, widget, expand=False):
+ tool_item = gtk.ToolItem()
+ tool_item.set_expand(expand)
+
+ tool_item.add(widget)
+ widget.show()
+
+ self.insert(tool_item, -1)
+ tool_item.show()
+
+ def __cancel_button_clicked_cb(self, widget, data=None):
+ self.emit('cancel-clicked')
+
+ def __accept_button_clicked_cb(self, widget, data=None):
+ self.emit('accept-clicked')
+
diff --git a/src/controlpanel/detailview.py b/src/controlpanel/detailview.py
new file mode 100644
index 0000000..abfe513
--- /dev/null
+++ b/src/controlpanel/detailview.py
@@ -0,0 +1,21 @@
+import gobject
+import gtk
+import gettext
+
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+class DetailView(gtk.VBox):
+ __gsignals__ = {
+ 'valid-section': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([bool]))
+ }
+ def __init__(self):
+ gtk.VBox.__init__(self)
+ self.restart = False
+ self.restart_alerts = []
+ self._restart_msg = _('Changes require a sugar restart to take effect.')
+
+ def undo(self):
+ '''Undo here the changes that have been made in this section.'''
+ pass
diff --git a/src/controlpanel/gui.py b/src/controlpanel/gui.py
new file mode 100644
index 0000000..98f93b6
--- /dev/null
+++ b/src/controlpanel/gui.py
@@ -0,0 +1,359 @@
+# Copyright (C) 2008 One Laptop Per Child
+#
+# 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 USA
+
+import gtk
+import gettext
+import os
+import gobject
+import logging
+
+from sugar.graphics.icon import Icon
+from sugar.graphics import style
+from sugar.graphics.alert import Alert
+import config
+
+from controlpanel.controltoolbar import MainToolbar
+from controlpanel.controltoolbar import DetailToolbar
+
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+
+class ControlPanel(gtk.Window):
+ __gtype_name__ = 'SugarControlPanel'
+
+ def __init__(self):
+ gtk.Window.__init__(self)
+
+ icons_path = os.path.join(config.prefix,
+ 'share/sugar/shell/controlpanel/icons')
+ gtk.icon_theme_get_default().append_search_path(icons_path)
+
+ self.set_border_width(style.LINE_WIDTH)
+ offset = style.GRID_CELL_SIZE
+ width = gtk.gdk.screen_width() - offset * 2
+ height = gtk.gdk.screen_height() - offset * 2
+ self.set_size_request(width, height)
+ self.set_position(gtk.WIN_POS_CENTER_ALWAYS)
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.set_modal(True)
+
+ self._toolbar = None
+ self._canvas = None
+ self._table = None
+ self._separator = None
+ self._detail_view = None
+ self._detail_toolbar = None
+ self._main_toolbar = None
+
+ self._vbox = gtk.VBox()
+ self._hbox = gtk.HBox()
+ self._vbox.pack_start(self._hbox)
+ self._hbox.show()
+
+ self._main_view = gtk.EventBox()
+ self._hbox.pack_start(self._main_view)
+ self._main_view.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_BLACK.get_gdk_color())
+ self._main_view.show()
+
+ self.add(self._vbox)
+ self._vbox.show()
+
+ self.connect("realize", self.__realize_cb)
+
+ self._options = {}
+ self._current_option = None
+ self._get_options()
+ self._setup_main()
+ self._setup_detail()
+ self._show_main_view()
+
+ def _update_accept_focus(self):
+ self.window.set_accept_focus(True)
+
+ def __realize_cb(self, widget):
+ self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+ self._update_accept_focus()
+
+ def _set_canvas(self, canvas):
+ if self._canvas:
+ self._main_view.remove(self._canvas)
+ if canvas:
+ self._main_view.add(canvas)
+ self._canvas = canvas
+
+ def _set_toolbar(self, toolbar):
+ if self._toolbar:
+ self._vbox.remove(self._toolbar)
+ self._vbox.pack_start(toolbar, False)
+ self._vbox.reorder_child(toolbar, 0)
+ self._toolbar = toolbar
+ if not self._separator:
+ self._separator = gtk.HSeparator()
+ self._vbox.pack_start(self._separator, False)
+ self._vbox.reorder_child(self._separator, 1)
+ self._separator.show()
+
+ def _setup_main(self):
+ self._main_toolbar = MainToolbar()
+
+ self._table = gtk.Table()
+ #self._table.set_row_spacings(style.DEFAULT_SPACING)
+ self._table.set_col_spacings(style.GRID_CELL_SIZE)
+ self._table.set_border_width(style.GRID_CELL_SIZE)
+ self._setup_options()
+ self._main_toolbar.connect('stop-clicked',
+ self.__stop_clicked_cb)
+ self._main_toolbar.connect('search-changed',
+ self.__search_changed_cb)
+
+ def _setup_options(self):
+ row = 0
+ column = 0
+ for option in self._options:
+ gridwidget = _GridWidget(icon_name=self._options[option]['icon'],
+ title=self._options[option]['title'],
+ xo_color = self._options[option]['color'],
+ pixel_size=style.GRID_CELL_SIZE)
+ gridwidget.connect('button_press_event',
+ self.__select_option_cb, option)
+ gridwidget.show()
+
+ self._table.attach(gridwidget, column, column+1, row, row+1)
+ self._options[option]['button'] = gridwidget
+
+ column += 1
+ if column == 5:
+ column = 0
+ row += 1
+
+ def _show_main_view(self):
+ self._set_toolbar(self._main_toolbar)
+ self._main_toolbar.show()
+ self._set_canvas(self._table)
+ self._main_view.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_BLACK.get_gdk_color())
+ self._table.show()
+ entry = self._main_toolbar.get_entry()
+ entry.grab_focus()
+ entry.set_text('')
+
+ def _update(self, query):
+ for option in self._options:
+ found = False
+ for key in self._options[option]['keywords']:
+ if query in key or query in key.upper() \
+ or query in key.capitalize():
+ self._options[option]['button'].set_sensitive(True)
+ found = True
+ break
+ if not found:
+ self._options[option]['button'].set_sensitive(False)
+
+ def _setup_detail(self):
+ self._detail_toolbar = DetailToolbar()
+ self._detail_toolbar.connect('cancel-clicked',
+ self.__cancel_clicked_cb)
+ self._detail_toolbar.connect('accept-clicked',
+ self.__accept_clicked_cb)
+
+ def _show_detail_view(self, option):
+ self._set_toolbar(self._detail_toolbar)
+
+ icon = self._detail_toolbar.get_icon()
+ icon.set_from_icon_name(self._options[option]['icon'],
+ gtk.ICON_SIZE_LARGE_TOOLBAR)
+ icon.props.xo_color = self._options[option]['color']
+ title = self._detail_toolbar.get_title()
+ title.set_text(self._options[option]['title'])
+ self._detail_toolbar.show()
+ self._detail_toolbar.accept_button.set_sensitive(True)
+
+ self._current_option = option
+ class_pointer = self._options[option]['view']
+ model = self._options[option]['model']
+ self._detail_view = class_pointer(model,
+ self._options[option]['alerts'])
+ self._set_canvas(self._detail_view)
+ self._detail_view.show()
+ self._detail_view.connect('valid-section', self.__valid_section_cb)
+ self._main_view.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+
+ def _get_options(self):
+ '''Get the available option information from the subfolders
+ model and view.
+ structure:
+ {'optionname': {'view', 'model', 'button', 'keywords', 'icon'} }
+ '''
+ path = os.path.join(config.prefix, 'share/sugar/shell')
+
+ subpath = ['controlpanel', 'view']
+ names = os.listdir(os.path.join(path, '/'.join(subpath)))
+ for name in names:
+ if name.endswith('.py') and name != '__init__.py':
+ tmp = name.strip('.py')
+ mod = __import__('.'.join(subpath) + '.' + tmp, globals(),
+ locals(), [tmp])
+ class_pointer = getattr(mod, tmp[0].capitalize()
+ + tmp[1:], None)
+ if class_pointer:
+ self._options[tmp] = {}
+ self._options[tmp]['alerts'] = []
+ self._options[tmp]['view'] = class_pointer
+ self._options[tmp]['icon'] = getattr(mod, 'ICON', tmp)
+ self._options[tmp]['title'] = getattr(mod, 'TITLE', tmp)
+ self._options[tmp]['color'] = getattr(mod, 'COLOR', None)
+
+ subpath = ['controlpanel', 'model']
+ names = os.listdir(os.path.join(path, '/'.join(subpath)))
+ for name in names:
+ if name.endswith('.py') and name != '__init__.py':
+ tmp = name.strip('.py')
+ if tmp in self._options:
+ mod = __import__('.'.join(subpath) + '.' + tmp,
+ globals(), locals(), [tmp])
+ keywords = getattr(mod, 'KEYWORDS', [])
+ keywords.append(self._options[tmp]['title'].lower())
+ if tmp not in keywords:
+ keywords.append(tmp)
+ self._options[tmp]['model'] = mod
+ self._options[tmp]['keywords'] = keywords
+
+ def __cancel_clicked_cb(self, widget, data=None):
+ self._detail_view.undo()
+ self._show_main_view()
+
+ def __accept_clicked_cb(self, widget, data=None):
+ if self._detail_view.restart:
+ self._detail_toolbar.accept_button.set_sensitive(False)
+ alert = Alert()
+ alert.props.title = _('Warning')
+ alert.props.msg = _('Changes require restart to take effect')
+
+ cancel_icon = Icon(icon_name='dialog-cancel')
+ alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), cancel_icon)
+ cancel_icon.show()
+
+ later_icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_ACCEPT, _('Later'), later_icon)
+ later_icon.show()
+
+ # TODO
+ # Handle restart
+
+ self._vbox.pack_start(alert, False)
+ self._vbox.reorder_child(alert, 2)
+ alert.connect('response', self.__response_cb)
+ alert.show()
+ else:
+ self._show_main_view()
+
+ def __response_cb(self, alert, response_id):
+ self._vbox.remove(alert)
+ if response_id is gtk.RESPONSE_CANCEL:
+ logging.debug('Cancel...')
+ self._detail_view.undo()
+ self._detail_toolbar.accept_button.set_sensitive(True)
+ elif response_id is gtk.RESPONSE_ACCEPT:
+ logging.debug('Later...')
+ self._options[self._current_option]['alerts'] = \
+ self._detail_view.restart_alerts
+ self._show_main_view()
+ elif response_id is gtk.RESPONSE_APPLY:
+ logging.debug('Restart...')
+
+ def __select_option_cb(self, button, event, option=None):
+ self._show_detail_view(option)
+
+ def __search_changed_cb(self, maintoolbar, query):
+ self._update(query)
+
+ def __stop_clicked_cb(self, widget, data=None):
+ self.destroy()
+
+ def __valid_section_cb(self, widget, valid):
+ self._detail_toolbar.accept_button.set_sensitive(valid)
+
+class _GridWidget(gtk.EventBox):
+ __gtype_name__ = "SugarGridWidget"
+
+ __gproperties__ = {
+ 'icon-name' : (str, None, None, None,
+ gobject.PARAM_READWRITE),
+ 'pixel-size' : (object, None, None,
+ gobject.PARAM_READWRITE),
+ 'xo-color' : (object, None, None,
+ gobject.PARAM_READWRITE),
+ 'title' : (str, None, None, None,
+ gobject.PARAM_READWRITE)
+ }
+
+ def __init__(self, **kwargs):
+ self.icon_name = None
+ self.pixel_size = style.GRID_CELL_SIZE
+ self.xo_color = None
+ self.title = 'No Title'
+
+ gobject.GObject.__init__(self, **kwargs)
+
+ self._vbox = gtk.VBox()
+ self._icon = Icon(icon_name=self.icon_name, pixel_size=self.pixel_size,
+ xo_color=self.xo_color)
+ self._vbox.pack_start(self._icon, expand=False, fill=False)
+
+ self._label = gtk.Label(self.title)
+ self._label.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+ self._vbox.pack_start(self._label, expand=False, fill=False)
+
+ self._vbox.set_spacing(style.DEFAULT_SPACING)
+ self.set_visible_window(False)
+ self.set_app_paintable(True)
+ self.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+
+ self.add(self._vbox)
+ self._vbox.show()
+ self._label.show()
+ self._icon.show()
+
+ def get_icon(self):
+ return self._icon
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'icon-name':
+ if self.icon_name != value:
+ self.icon_name = value
+ elif pspec.name == 'pixel-size':
+ if self.pixel_size != value:
+ self.pixel_size = value
+ elif pspec.name == 'xo-color':
+ if self.xo_color != value:
+ self.xo_color = value
+ elif pspec.name == 'title':
+ if self.title != value:
+ self.title = value
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'icon-name':
+ return self.icon_name
+ elif pspec.name == 'pixel-size':
+ return self.pixel_size
+ elif pspec.name == 'xo-color':
+ return self.xo_color
+ elif pspec.name == 'title':
+ return self.title
diff --git a/src/controlpanel/icons/Makefile.am b/src/controlpanel/icons/Makefile.am
new file mode 100644
index 0000000..1d5d478
--- /dev/null
+++ b/src/controlpanel/icons/Makefile.am
@@ -0,0 +1,10 @@
+sugardir = $(pkgdatadir)/shell/controlpanel/icons
+
+sugar_DATA = \
+ module-about_me.svg \
+ module-about_my_xo.svg \
+ module-date_and_time.svg \
+ module-language.svg \
+ module-network.svg
+
+EXTRA_DIST = $(sugar_DATA) \ No newline at end of file
diff --git a/src/controlpanel/icons/module-about_me.svg b/src/controlpanel/icons/module-about_me.svg
new file mode 100644
index 0000000..7abe926
--- /dev/null
+++ b/src/controlpanel/icons/module-about_me.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#ffffff">
+]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="module-about_x5F_me_1_">
+ <path d="M33.359,35.101L43.46,45.201c0.752,0.75,1.217,1.784,1.217,2.932 c0,2.287-1.855,4.143-4.146,4.143c-1.145,0-2.178-0.463-2.932-1.211L27.498,40.963l-10.1,10.1c-0.75,0.75-1.787,1.211-2.933,1.211 c-2.285,0-4.143-1.854-4.143-4.141c0-1.146,0.465-2.184,1.212-2.934l10.104-10.101L11.535,24.997 c-0.747-0.749-1.212-1.785-1.212-2.93c0-2.289,1.854-4.145,4.146-4.145c1.143,0,2.18,0.465,2.93,1.214l10.099,10.101l10.101-10.102 c0.754-0.749,1.787-1.214,2.934-1.214c2.289,0,4.146,1.856,4.146,4.145c0,1.145-0.467,2.179-1.217,2.93L33.359,35.101z" fill="&fill_color;" stroke="&stroke_color;" stroke-width="3.5"/>
+ <circle cx="27.497" cy="10.849" fill="&fill_color;" r="8.122" stroke="&stroke_color;" stroke-width="3.5"/>
+</g></svg> \ No newline at end of file
diff --git a/src/controlpanel/icons/module-about_my_xo.svg b/src/controlpanel/icons/module-about_my_xo.svg
new file mode 100644
index 0000000..cf3528e
--- /dev/null
+++ b/src/controlpanel/icons/module-about_my_xo.svg
@@ -0,0 +1,6 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#ffffff">
+]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="module-about_x5F_my_x5F_xo_1_">
+ <path d="M52.957,40.602h0.002l-0.025-0.017 c-0.152-0.11-0.315-0.21-0.483-0.302l-12.204-7.605V8.667c0-1.624-1.316-2.943-2.941-2.943H6.291c-1.625,0-2.942,1.319-2.942,2.943 V35.08c0,1.1,0.61,2.045,1.503,2.551l-0.019,0.004L19.49,46.77c0.694,0.436,1.534,0.691,2.438,0.691h28.319 c2.362,0,4.296-1.74,4.296-3.865C54.543,42.391,53.923,41.312,52.957,40.602z M9.072,12.392c0-0.619,0.506-1.124,1.124-1.124H33.4 c0.617,0,1.123,0.505,1.123,1.124v16.561c0,0.617-0.506,1.126-1.123,1.126H10.196c-0.617,0-1.124-0.509-1.124-1.126V12.392z" display="inline" fill="&fill_color;" id="module-about_x5F_my_x5F_xo"/>
+</g></svg> \ No newline at end of file
diff --git a/src/controlpanel/icons/module-date_and_time.svg b/src/controlpanel/icons/module-date_and_time.svg
new file mode 100644
index 0000000..605dbeb
--- /dev/null
+++ b/src/controlpanel/icons/module-date_and_time.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#ffffff">
+]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="module-date_x5F_and_x5F_time">
+ <g display="inline">
+ <defs>
+ <path d="M29.891,31.641h17.255c0.346-1.563,0.534-3.187,0.534-4.854c0-12.35-10.013-22.362-22.362-22.362 c-12.351,0-22.362,10.012-22.362,22.362c0,12.351,10.011,22.362,22.362,22.362c1.567,0,3.097-0.163,4.573-0.47V31.641z M26.286,28.242c-0.034,0.022-0.071,0.038-0.107,0.058c-0.064,0.037-0.127,0.075-0.196,0.104 c-0.039,0.016-0.079,0.023-0.118,0.036c-0.069,0.023-0.138,0.049-0.21,0.062c-0.048,0.01-0.098,0.01-0.147,0.015 c-0.063,0.007-0.126,0.02-0.191,0.02c-0.071,0-0.139-0.013-0.208-0.021c-0.043-0.006-0.086-0.005-0.129-0.014 c-0.078-0.015-0.152-0.041-0.226-0.066c-0.034-0.012-0.069-0.019-0.102-0.032c-0.077-0.031-0.147-0.072-0.217-0.113 c-0.028-0.017-0.059-0.028-0.086-0.047c-0.193-0.129-0.359-0.294-0.487-0.487c-0.028-0.042-0.047-0.086-0.071-0.13 c-0.031-0.057-0.065-0.111-0.09-0.171c-0.023-0.056-0.037-0.115-0.054-0.173c-0.015-0.051-0.035-0.101-0.045-0.153 c-0.023-0.114-0.035-0.229-0.035-0.344V10.349c0-0.966,0.783-1.75,1.75-1.75s1.75,0.784,1.75,1.75v12.212l3.973-3.973 c0.684-0.683,1.792-0.683,2.476,0c0.683,0.683,0.683,1.792,0,2.475l-6.96,6.96c-0.001,0.002-0.003,0.003-0.005,0.004 C26.469,28.107,26.381,28.179,26.286,28.242z" id="SVGID_5_"/>
+ </defs>
+ <clipPath id="SVGID_6_">
+ <use overflow="visible" xlink:href="#SVGID_5_"/>
+ </clipPath>
+ <circle clip-path="url(#SVGID_6_)" cx="25.318" cy="26.786" fill="&fill_color;" r="22.362"/>
+ </g>
+ <rect display="inline" fill="none" height="19.319" stroke="&fill_color;" stroke-width="2" width="21.064" x="29.891" y="31.641"/>
+ <g display="inline">
+ <path d="M39.056,44.155c0.527,0,0.936,0.239,0.936,0.792c0,0.551-0.408,0.791-0.864,0.791h-4.006 c-0.527,0-0.936-0.24-0.936-0.791c0-0.252,0.156-0.469,0.276-0.612c0.995-1.188,2.075-2.267,2.986-3.586 c0.216-0.312,0.42-0.684,0.42-1.115c0-0.491-0.372-0.924-0.863-0.924c-1.38,0-0.72,1.943-1.871,1.943 c-0.575,0-0.876-0.408-0.876-0.876c0-1.511,1.344-2.723,2.818-2.723c1.476,0,2.663,0.972,2.663,2.495 c0,1.667-1.858,3.322-2.878,4.605H39.056z" fill="&fill_color;"/>
+ <path d="M46.339,39.25c0,0.756-0.323,1.415-0.983,1.835c0.863,0.396,1.463,1.199,1.463,2.146 c0,1.439-1.318,2.651-3.021,2.651c-1.775,0-2.879-1.309-2.879-2.256c0-0.467,0.492-0.803,0.924-0.803 c0.815,0,0.623,1.402,1.979,1.402c0.623,0,1.127-0.479,1.127-1.115c0-1.679-2.039-0.443-2.039-1.858 c0-1.259,1.703-0.408,1.703-1.739c0-0.455-0.323-0.804-0.863-0.804c-1.139,0-0.982,1.176-1.799,1.176 c-0.492,0-0.779-0.444-0.779-0.888c0-0.936,1.283-1.943,2.614-1.943C45.512,37.055,46.339,38.314,46.339,39.25z" fill="&fill_color;"/>
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/src/controlpanel/icons/module-language.svg b/src/controlpanel/icons/module-language.svg
new file mode 100644
index 0000000..ce04cb4
--- /dev/null
+++ b/src/controlpanel/icons/module-language.svg
@@ -0,0 +1,59 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#ffffff">
+]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="module-language">
+ <g display="inline">
+ <g>
+ <path clip-rule="evenodd" d="M35.805,13.962c-0.346-0.084-0.779-0.072-0.725-0.6 c0.24,0,0.482,0,0.725,0l-0.092,0.301L35.805,13.962z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M36.438,14.063l0.09-0.3c1.146-0.067,1.871,0.331,2.533,0.8 c3.684,0.596,9.051-0.669,10.676,2.2c-0.164,0.485-0.727,0.53-1.268,0.6c0.531,0.215,0.637,0.898,0.543,1.8 c-0.928-0.174-1.295-0.968-1.447-2c-0.553,0.389-1.396,0.457-2.352,0.4c-0.121,0.867,0.84,0.539,1.447,0.6 c0.057,0.472,0.629,0.373,0.543,1l-0.271,0.1l0.09,0.3c-0.549,0.658-0.812,1.635-0.723,3c-0.551,0.01-0.703-0.422-1.086-0.6 c0.26,1.426,0.908,1.89,0.723,3.4c-1.1-1.013-1.793,1.516-1.99,2.799c-0.627,0.161-0.693-0.297-1.266-0.199l-0.182-0.1l-0.18,0.1 c-0.475-1.659-2.096-3.383-3.076-1c0,0.4,0,0.8,0,1.199l-0.271-0.1l0.09,0.301c-1.533,0.073-0.889-3.936-3.438-3.4 c-0.357,1.338-1.098,2.254-2.535,2.4c-0.164,0.784,0.955,0.145,1.086,0.6c-0.146,1.638-1.449,1.998-1.99,3.2 c0.646,2.376-0.838,2.551-0.902,4.601c-0.834,0.479-0.881,1.828-1.811,2.2c-3.492-0.651-1.328-6.379-3.258-8.601 c-0.848-0.263-1.984-0.205-3.075-0.2c-2.032-1.712-0.716-5.163,0.724-6.8c-0.372-0.323-0.419-1.003-0.362-1.801 c0.481-0.002,1.034,0.076,1.267-0.2c-0.148-0.703-0.412-1.279-1.267-1.201c0.081-0.912,0.522-1.423,1.267-1.6 c0.269,0.237,0.282,0.755,0.543,1c0.512,0.165,0.469-0.282,0.904-0.201c0.191-0.877-0.611-0.657-0.543-1.4 c0.357-0.828,1.436-1.889,2.354-2c1.213-0.147,1.916,0.831,3.617,0.6c0.695-0.094,1.172-0.681,2.172-1 c0.594-0.19,1.852,0.085,2.354-0.601L36.438,14.063z M27.842,17.963c0.438-0.392,0.416-1-0.18-1.2 C27.766,17.115,27.441,17.938,27.842,17.963z M27.48,21.563c-0.186-0.944-2.604-0.928-2.353,0.4c0.483,0,0.966,0,1.448,0 l0.09,0.3l0.271-0.101c0,0.066,0,0.134,0,0.201c0.442,1.067,3.484,1.27,3.98,0c-1.439,0.273-2.379-0.674-3.256-0.4l0.09-0.301 L27.48,21.563z M32.365,26.764c0.031-0.501-0.277-0.626-0.541-0.8C31.791,26.464,32.102,26.59,32.365,26.764z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M48.018,21.863l0.09-0.3c0.316,0.184,0.4,0.625,0.361,1.2 c-0.547,0.261-1.043,0.579-1.627,0.8c-0.492-1.168,0.654-0.949,0.904-1.8L48.018,21.863z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M42.77,28.864l0.092-0.301c0.662,0.467,0.711,1.615,0.904,2.601 l-0.332-0.1l-0.211,0.3c-0.619-0.582-0.996-1.434-1.268-2.4c0.146,0.105,0.295,0.209,0.543,0.2c0-0.134,0-0.267,0-0.399 L42.77,28.864z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M11.376,25.163c0.423,0.067,0.78,0.206,0.905,0.601 l-0.271,0.233l0.09,0.367c-0.21-0.033-0.322,0.044-0.361,0.199c-0.465-0.02-0.291-0.744-0.543-1l0.271-0.1L11.376,25.163z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M46.299,27.764c-0.281-0.29-0.381-0.78-0.363-1.399 c0.182,0,0.363,0,0.545,0c0.104,0.418,0.447,0.57,0.361,1.199l-0.332-0.1L46.299,27.764z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M45.936,30.364c0.131,0.877-0.602,0.8-1.266,0.8 c-0.264-0.44-0.549-0.858-0.543-1.601c0.738-0.051,0.895-0.744,1.809-0.6c-0.057,0.465,0.029,0.768,0.182,1l-0.271,0.101 L45.936,30.364z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M46.389,30.265l-0.09-0.301c0.42-0.063,0.693,0.033,0.904,0.2 c-0.332,0.501-0.145,1.574-1.086,1.4c0-0.4,0-0.8,0-1.2L46.389,30.265z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M48.65,31.164c-0.553,0.146-0.531-0.345-1.086-0.199 c-0.086-0.716,1.123-1.144,1.268-0.4l-0.271,0.233L48.65,31.164z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M49.104,30.931l-0.092-0.366c0.926-0.487,1.51,0.948,2.715,0.8 c-0.617,0.261-0.084,0.855-0.361,1.199" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M49.918,32.765c0.291,0.117,1.43,2.391,1.447,3 c0.045,1.674-1.633,3.377-2.715,3.201c-0.922-0.152-0.688-0.955-1.447-1.601c-0.98,0.12-1.854,0.957-2.895,0.399 c-0.109-1.053,0.133-1.718,0.18-2.6c1.658-0.205,3.107-3.646,4.705-1.4C49.775,33.807,49.633,33.051,49.918,32.765z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M33.451,33.164c0.725,1.004,0.084,2.368-0.725,2.8 C32.002,34.962,32.742,33.707,33.451,33.164z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M48.65,39.165c0.383,0.062,0.039,0.948-0.361,0.8 C48.082,39.336,48.572,39.479,48.65,39.165z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M16.805,12.962c2.05,0,4.102,0,6.15,0 c0.007,0.195,0.223,0.155,0.363,0.2c0.324,0.701-0.65,1.677-1.084,2.2c-1.523,0.317-2.302,1.457-3.621,2 c-0.971-0.425-0.523-1.94-0.724-2.8c-0.439-0.464-2.139-0.687-2.532,0c1.822-0.026,1.732,2.399,0.18,2.6 c1.181-0.023,1.775,2.452,0.543,2.8l0.091-0.3l-0.272-0.1c-0.099-0.657-1.186,0.234-0.541,0.4l-0.092,0.3l0.271,0.101 c-2.093,0.085-2.461,2.079-3.98,2.799c0,0.201,0,0.4,0,0.601l-0.332-0.1l-0.21,0.3c-0.761-0.482-2.09-0.11-2.534,0.399 c0,0.534,0,1.067,0,1.601c0.622,0.076,1.287-0.828,1.628-0.4c-0.564,1.371,0.746,1.471,0.906,2.6l-0.272,0.201l0.272,0.199 c-0.433,0.062-1.751-0.617-2.534-1.199C7.746,26.816,7,25.966,6.673,25.163c-0.38-0.229-0.707,0.123-1.086-0.399 c-0.229-1.306-0.774-2.063-0.724-3.2c0.032-0.728,0.597-0.782,0.724-1.601c0.033-0.217-0.352-0.856-0.362-1.399 c-0.01-0.526,0.43-0.783,0-1.201c-1.454-0.274-1.845,0.627-3.076,0.6l0.09-0.199l-0.09-0.2c-0.231-0.521-0.397-0.604-0.182-1.2 c1.83-1.044,4.094-1.609,7.057-1.4c0.688-0.04,0.687-0.841,1.448-0.8c1.325-0.132,2.296,0.13,3.257,0.4 c0.705-0.021,0.311-1.256,0.904-1.4C15.303,13.035,16.436,13.422,16.805,12.962z M14.452,15.963 c0.286-0.02,0.628,0.027,0.542-0.4c-0.119,0-0.24,0-0.361,0C14.633,15.764,14.486,15.801,14.452,15.963z M11.92,17.763 c0.789-0.139,0.803,0.577,1.447,0.6c0.266-0.505,0.639-0.893,0.723-1.6C13.093,16.545,11.914,16.393,11.92,17.763z" fill="&fill_color;" fill-rule="evenodd"/>
+ <path clip-rule="evenodd" d="M11.467,28.364l-0.091-0.201 c1.866-1.496,4.541,0.503,5.247,2.201c0.822,0.426,2.057,0.393,2.352,1.4c-0.152,1.164-0.635,1.965-0.723,3.199 c-0.226,0.484-0.742,0.648-1.268,0.801c-0.201,2.777-2.827,3.017-2.17,6.201c0.453-0.035,0.566,0.307,0.723,0.6 c-0.36,0-0.723,0-1.085,0c-1.601-1.566-1.526-4.982-1.81-8.001c-1.498-1.305-2.468-3.841-1.267-6.001L11.467,28.364z" fill="&fill_color;" fill-rule="evenodd"/>
+ <g>
+
+ <path clip-rule="evenodd" d=" M48.018,21.863" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1637"/>
+
+ <path clip-rule="evenodd" d=" M46.932,19.263" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1637"/>
+
+ <path clip-rule="evenodd" d=" M43.434,31.064" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.8752"/>
+
+ <path clip-rule="evenodd" d=" M42.498,27.864" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1506"/>
+
+ <path clip-rule="evenodd" d=" M38.971,28.063" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="1.2247"/>
+
+ <path clip-rule="evenodd" d=" M26.851,21.793" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1448"/>
+
+ <path clip-rule="evenodd" d=" M12.01,25.997" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.8087"/>
+
+ <path clip-rule="evenodd" d=" M46.51,27.464" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="3.5214"/>
+
+ <path clip-rule="evenodd" d=" M46.752,28.264" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="3.5214"/>
+
+ <path clip-rule="evenodd" d=" M45.846,30.064" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1602"/>
+
+ <path clip-rule="evenodd" d=" M49.104,30.931" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="3.1091"/>
+
+ <path clip-rule="evenodd" d=" M46.752,32.165" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.1381"/>
+
+ <path clip-rule="evenodd" d=" M15.266,20.263" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="1.6279"/>
+
+ <path clip-rule="evenodd" d=" M11.225,23.664" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.9155"/>
+
+ <path clip-rule="evenodd" d=" M11.467,28.364" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.0976"/>
+
+ <path clip-rule="evenodd" d=" M10.743,28.364" fill="&fill_color;" fill-rule="evenodd" stroke="#000000" stroke-linecap="round" stroke-width="2.0976"/>
+ </g>
+ </g>
+ </g>
+ <rect display="inline" fill="none" height="30.334" stroke="&fill_color;" stroke-width="3.5" width="50.67" x="2.25" y="12.625"/>
+</g></svg> \ No newline at end of file
diff --git a/src/controlpanel/icons/module-network.svg b/src/controlpanel/icons/module-network.svg
new file mode 100644
index 0000000..a750a38
--- /dev/null
+++ b/src/controlpanel/icons/module-network.svg
@@ -0,0 +1,32 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#666666">
+ <!ENTITY fill_color "#ffffff">
+]><svg enable-background="new 0 0 55 55" height="55px" id="Layer_1" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="module-network">
+ <g display="inline">
+ <defs>
+ <path d="M14.897,34.339c0-10.144,8.224-18.367,18.367-18.367c3.929,0,7.562,1.244,10.549,3.345V0H0v43.812h17.55 C15.877,41.044,14.897,37.81,14.897,34.339z" id="SVGID_1_"/>
+ </defs>
+ <clipPath id="SVGID_2_">
+ <use overflow="visible" xlink:href="#SVGID_1_"/>
+ </clipPath>
+ <g clip-path="url(#SVGID_2_)">
+
+ <circle cx="21.47" cy="21.073" fill="none" r="18.368" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"/>
+
+ <circle cx="21.469" cy="21.073" fill="none" r="10.476" stroke="&fill_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="4"/>
+ <circle cx="21.469" cy="21.073" fill="&fill_color;" r="3.966"/>
+ </g>
+ </g>
+ <g display="inline">
+ <g>
+ <defs>
+ <rect height="18.367" id="SVGID_3_" width="36.736" x="14.897" y="34.339"/>
+ </defs>
+ <clipPath id="SVGID_4_">
+ <use overflow="visible" xlink:href="#SVGID_3_"/>
+ </clipPath>
+ <circle clip-path="url(#SVGID_4_)" cx="33.265" cy="34.339" fill="&fill_color;" r="18.368"/>
+ </g>
+ <circle cx="33.265" cy="34.339" fill="none" r="18.368" stroke="&fill_color;" stroke-width="3.5"/>
+ </g>
+</g></svg> \ No newline at end of file
diff --git a/src/controlpanel/inlinealert.py b/src/controlpanel/inlinealert.py
new file mode 100644
index 0000000..ff14453
--- /dev/null
+++ b/src/controlpanel/inlinealert.py
@@ -0,0 +1,72 @@
+import gtk
+import gobject
+import pango
+
+from sugar.graphics import style
+
+class InlineAlert(gtk.EventBox):
+ """UI interface for Inline alerts
+
+ Alerts are used inside the activity window instead of being a
+ separate popup window. They do not hide canvas content. You can
+ use add_alert(widget) and remove_alert(widget) inside your activity
+ to add and remove the alert. The position of the alert is below the
+ toolbox or top in fullscreen mode.
+
+ Properties:
+ 'message': the message of the alert,
+ 'icon': the icon that appears at the far left
+ See __gproperties__
+ """
+
+ __gtype_name__ = 'SugarInlineAlert'
+
+ __gproperties__ = {
+ 'msg' : (str, None, None, None,
+ gobject.PARAM_READWRITE),
+ 'msg' : (str, None, None, None,
+ gobject.PARAM_READWRITE),
+ 'icon' : (object, None, None,
+ gobject.PARAM_WRITABLE)
+ }
+
+ def __init__(self, **kwargs):
+
+ self._msg = None
+ self._msg_color = None
+ self._icon = None
+
+ self._hbox = gtk.HBox()
+ self._hbox.set_spacing(style.DEFAULT_SPACING)
+
+ self._msg_label = gtk.Label()
+ self._msg_label.set_max_width_chars(50)
+ self._msg_label.set_ellipsize(pango.ELLIPSIZE_MIDDLE)
+ self._msg_label.set_alignment(0, 0.5)
+ self._msg_label.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ self._hbox.pack_start(self._msg_label, False)
+
+ gobject.GObject.__init__(self, **kwargs)
+
+ self.set_visible_window(True)
+ self.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+ self.add(self._hbox)
+ self._msg_label.show()
+ self._hbox.show()
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'msg':
+ if self._msg != value:
+ self._msg = value
+ self._msg_label.set_markup(self._msg)
+ elif pspec.name == 'icon':
+ if self._icon != value:
+ self._icon = value
+ self._hbox.pack_start(self._icon, False)
+ self._hbox.reorder_child(self._icon, 0)
+
+ def do_get_property(self, pspec):
+ if pspec.name == 'msg':
+ return self._msg
diff --git a/src/controlpanel/view/Makefile.am b/src/controlpanel/view/Makefile.am
new file mode 100644
index 0000000..faffc20
--- /dev/null
+++ b/src/controlpanel/view/Makefile.am
@@ -0,0 +1,9 @@
+sugardir = $(pkgdatadir)/shell/controlpanel/view
+sugar_PYTHON = \
+ __init__.py \
+ network.py \
+ aboutme.py \
+ language.py \
+ timezone.py \
+ aboutxo.py
+
diff --git a/src/controlpanel/view/__init__.py b/src/controlpanel/view/__init__.py
new file mode 100644
index 0000000..2b0f269
--- /dev/null
+++ b/src/controlpanel/view/__init__.py
@@ -0,0 +1,17 @@
+# Copyright (C) 2008 One Laptop Per Child
+#
+# 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 USA
+#
+
diff --git a/src/controlpanel/view/aboutme.py b/src/controlpanel/view/aboutme.py
new file mode 100644
index 0000000..99e049f
--- /dev/null
+++ b/src/controlpanel/view/aboutme.py
@@ -0,0 +1,231 @@
+# Copyright (C) 2008, OLPC
+#
+# 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 USA
+
+import gtk
+import gettext
+import gobject
+
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics.icon import Icon
+from sugar.graphics import style
+from sugar.graphics.xocolor import XoColor
+from sugar import profile
+
+from controlpanel.detailview import DetailView
+from controlpanel.inlinealert import InlineAlert
+
+ICON = 'module-about_me'
+COLOR = profile.get_color()
+TITLE = _('About Me')
+
+class EventIcon(gtk.EventBox):
+ __gtype_name__ = "SugarEventIcon"
+ def __init__(self, **kwargs):
+ gtk.EventBox.__init__(self)
+
+ self.icon = Icon(pixel_size = style.XLARGE_ICON_SIZE, **kwargs)
+
+ self.set_visible_window(False)
+ self.set_app_paintable(True)
+ self.set_events(gtk.gdk.BUTTON_PRESS_MASK)
+
+ self.add(self.icon)
+ self.icon.show()
+
+class ColorPicker(EventIcon):
+ __gsignals__ = {
+ 'color-changed': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([object]))
+ }
+ def __init__(self, xocolor):
+ EventIcon.__init__(self)
+ self.icon.props.xo_color = xocolor
+ self.icon.props.icon_name = 'computer-xo'
+ self.icon.props.pixel_size = style.XLARGE_ICON_SIZE
+ self.connect('button_press_event', self.__pressed_cb)
+
+ def __pressed_cb(self, button, event):
+ self._set_random_colors()
+
+ def _set_random_colors(self):
+ xocolor = XoColor()
+ self.icon.props.xo_color = xocolor
+ self.emit('color-changed', xocolor)
+
+class Aboutme(DetailView):
+ def __init__(self, model, alerts):
+ DetailView.__init__(self)
+
+ self.emit('valid_section', True)
+
+ self._model = model
+ self.restart_alerts = alerts
+ self._nick = self._model.get_nick()
+ self._xocolor = self._model.get_color()
+
+ 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)
+
+ group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ self._nick_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self._nick_alert_box = 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.pack_start(self._nick_box, False)
+ self.pack_start(self._nick_alert_box, False)
+ self.pack_start(self._color_box, False)
+ self.pack_start(self._color_alert_box, False)
+
+ label_entry = gtk.Label(_('Name:'))
+ label_entry.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ group.add_widget(label_entry)
+ label_entry.set_alignment(1, 0.5)
+ self._nick_box.pack_start(label_entry, expand=False)
+ label_entry.show()
+
+ label_entry_error = gtk.Label()
+ group.add_widget(label_entry_error)
+ self._nick_alert_box.pack_start(label_entry_error, expand=False)
+ label_entry_error.show()
+ icon = Icon(icon_name='emblem-warning',
+ fill_color=style.COLOR_SELECTION_GREY.get_svg(),
+ stroke_color=style.COLOR_WHITE.get_svg())
+ self._nick_alert = InlineAlert(icon=icon)
+ icon.show()
+ self._nick_alert_box.pack_start(self._nick_alert)
+ if 'nick' in self.restart_alerts:
+ self._nick_alert.props.msg = self._restart_msg
+ self._nick_alert.show()
+
+ self._entry = gtk.Entry()
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.set_text(self._nick)
+ self._entry.set_width_chars(25)
+ self._entry.connect('changed', self.__nick_changed_cb)
+ self._nick_box.pack_start(self._entry, expand=False)
+ self._entry.show()
+
+ label_color = gtk.Label(_('Click to change your color:'))
+ label_color.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ group.add_widget(label_color)
+ self._color_box.pack_start(label_color, expand=False)
+ label_color.show()
+
+ self._col = ColorPicker(self._xocolor)
+ self._col.connect('color-changed', self.__color_changed_cb)
+ self._color_box.pack_start(self._col, expand=False)
+ self._col.show()
+
+ label_color_error = gtk.Label()
+ group.add_widget(label_color_error)
+ self._color_alert_box.pack_start(label_color_error, expand=False)
+ label_color_error.show()
+ icon = Icon(icon_name='emblem-warning',
+ fill_color=style.COLOR_SELECTION_GREY.get_svg(),
+ stroke_color=style.COLOR_WHITE.get_svg())
+ self._color_alert = InlineAlert(icon=icon)
+ icon.show()
+ self._color_alert_box.pack_start(self._color_alert)
+ if 'color' in self.restart_alerts:
+ self._color_alert.props.msg = self._restart_msg
+ self._color_alert.show()
+
+ self._nick_box.show()
+ self._color_box.show()
+ self._nick_alert_box.show()
+ self._color_alert_box.show()
+
+ def undo(self):
+ self._model.set_nick(self._nick)
+ self._model.set_color_xo(self._xocolor)
+
+ self._entry.set_text(self._nick)
+ self._col.icon.props.xo_color = self._xocolor
+ if self._color_alert.props.visible:
+ self._color_alert.hide()
+
+ self._nick_valid = True
+ self._color_valid = True
+ self.restart = False
+ self.restart_alerts = []
+
+ def __nick_changed_cb(self, widget, data=None):
+ if self._nick_sid:
+ gobject.source_remove(self._nick_sid)
+ self._nick_sid = gobject.timeout_add(1000, self.__nick_timeout_cb,
+ widget)
+
+ def __nick_timeout_cb(self, widget):
+ self._nick_sid = 0
+ try:
+ self._model.set_nick(widget.get_text())
+ except ValueError, detail:
+ self._nick_alert.props.msg = detail
+ self._nick_valid = False
+ else:
+ self._nick_alert.props.msg = self._restart_msg
+ self._nick_valid = True
+ if widget.get_text() != self._nick:
+ self.restart = True
+ self.restart_alerts.append('nick')
+ else:
+ self.restart = False
+
+ if self._nick_valid and self._color_valid:
+ self.emit('valid_section', True)
+ else:
+ self.emit('valid_section', False)
+
+ if not self._nick_alert.props.visible or \
+ widget.get_text() != self._nick:
+ self._nick_alert.show()
+ else:
+ self._nick_alert.hide()
+
+ return False
+
+ def __color_changed_cb(self, colorpicker, xocolor):
+ self._model.set_color_xo(xocolor)
+ self.restart = True
+ self._color_alert.props.msg = self._restart_msg
+ self._color_valid = True
+ self.restart_alerts.append('color')
+
+ if self._nick_valid and self._color_valid:
+ self.emit('valid_section', True)
+ else:
+ self.emit('valid_section', False)
+
+ if not self._color_alert.props.visible:
+ self._color_alert.show()
+
+
+
+
+
+
diff --git a/src/controlpanel/view/aboutxo.py b/src/controlpanel/view/aboutxo.py
new file mode 100644
index 0000000..0df0871
--- /dev/null
+++ b/src/controlpanel/view/aboutxo.py
@@ -0,0 +1,120 @@
+import re
+import os
+import gtk
+import gettext
+import logging
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics import style
+
+from controlpanel.detailview import DetailView
+
+ICON = 'module-about_my_xo'
+TITLE = _('About my XO')
+
+class Aboutxo(DetailView):
+ def __init__(self, model=None, alerts=None):
+ DetailView.__init__(self)
+
+ self.set_border_width(style.DEFAULT_SPACING * 2)
+ self.set_spacing(style.DEFAULT_SPACING)
+ not_available = _('Not available')
+ group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ separator_identity = gtk.HSeparator()
+ self.pack_start(separator_identity, expand=False)
+ separator_identity.show()
+ label_identity = gtk.Label(_('Identity'))
+ label_identity.set_alignment(0, 0)
+ self.pack_start(label_identity, expand=False)
+ label_identity.show()
+ vbox_identity = gtk.VBox()
+ vbox_identity.set_border_width(style.DEFAULT_SPACING * 2)
+ vbox_identity.set_spacing(style.DEFAULT_SPACING)
+ box_identity = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label_serial = gtk.Label(_('Serial Number:'))
+ label_serial.set_alignment(1, 0)
+ label_serial.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ box_identity.pack_start(label_serial, expand=False)
+ group.add_widget(label_serial)
+ label_serial.show()
+ serial_no = self._read_file('/ofw/serial-number')
+ if serial_no is None:
+ serial_no = not_available
+ label_serial_no = gtk.Label(serial_no)
+ label_serial_no.set_alignment(0, 0)
+ box_identity.pack_start(label_serial_no, expand=False)
+ label_serial_no.show()
+ vbox_identity.pack_start(box_identity, expand=False)
+ box_identity.show()
+ self.pack_start(vbox_identity, expand=False)
+ vbox_identity.show()
+
+ separator_software = gtk.HSeparator()
+ self.pack_start(separator_software, expand=False)
+ separator_software.show()
+ label_software = gtk.Label(_('Software'))
+ label_software.set_alignment(0, 0)
+ self.pack_start(label_software, expand=False)
+ label_software.show()
+ box_software = gtk.VBox()
+ box_software.set_border_width(style.DEFAULT_SPACING * 2)
+ box_software.set_spacing(style.DEFAULT_SPACING)
+ box_build = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label_build = gtk.Label(_('Build:'))
+ label_build.set_alignment(1, 0)
+ label_build.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ box_build.pack_start(label_build, expand=False)
+ group.add_widget(label_build)
+ label_build.show()
+ build_no = self._read_file('/boot/olpc_build')
+ if build_no is None:
+ build_no = not_available
+ label_build_no = gtk.Label(build_no)
+ label_build_no.set_alignment(0, 0)
+ box_build.pack_start(label_build_no, expand=False)
+ label_build_no.show()
+ box_software.pack_start(box_build, expand=False)
+ box_build.show()
+
+ box_firmware = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label_firmware = gtk.Label(_('Firmware:'))
+ label_firmware.set_alignment(1, 0)
+ label_firmware.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ box_firmware.pack_start(label_firmware, expand=False)
+ group.add_widget(label_firmware)
+ label_firmware.show()
+ firmware_no = self._read_file('/ofw/openprom/model')
+ if firmware_no is None:
+ firmware_no = not_available
+ else:
+ firmware_no = re.split(" +", firmware_no)
+ if len(firmware_no) == 3:
+ firmware_no = firmware_no[1]
+ label_firmware_no = gtk.Label(firmware_no)
+ label_firmware_no.set_alignment(0, 0)
+ box_firmware.pack_start(label_firmware_no, expand=False)
+ label_firmware_no.show()
+ box_software.pack_start(box_firmware, expand=False)
+ box_firmware.show()
+ self.pack_start(box_software, expand=False)
+ box_software.show()
+
+ def _read_file(self, path):
+ if os.access(path, os.R_OK) == 0:
+ logging.error('read_file() No such file or directory: %s', path)
+ return None
+
+ fd = open(path, 'r')
+ value = fd.read()
+ fd.close()
+ if value:
+ value = value.strip('\n')
+ return value
+ else:
+ logging.error('read_file() No information in file or directory: %s'
+ , path)
+ return None
diff --git a/src/controlpanel/view/language.py b/src/controlpanel/view/language.py
new file mode 100644
index 0000000..55b3f7f
--- /dev/null
+++ b/src/controlpanel/view/language.py
@@ -0,0 +1,109 @@
+import gtk
+import gobject
+import gettext
+
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics import style
+from sugar.graphics import iconentry
+
+from controlpanel.detailview import DetailView
+
+ICON = 'module-language'
+TITLE = _('Language')
+
+class Language(DetailView):
+ def __init__(self, model, alerts):
+ DetailView.__init__(self)
+ self._model = model
+
+ self.set_border_width(style.DEFAULT_SPACING * 2)
+ self.set_spacing(style.DEFAULT_SPACING)
+
+ self.connect("realize", self.__realize_cb)
+
+ self.restart = False
+ self._lang_sid = 0
+ self._lang = self._model.get_language()
+ self._lang_set = self._lang
+
+ self._entry = iconentry.IconEntry()
+ self._entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
+ 'system-search')
+ self._entry.add_clear_button()
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self.pack_start(self._entry, False)
+ self._entry.show()
+
+ self._scrolled_window = gtk.ScrolledWindow()
+ self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self._scrolled_window.set_shadow_type(gtk.SHADOW_IN)
+
+ self._store = gtk.ListStore(gobject.TYPE_STRING,
+ gobject.TYPE_STRING)
+ locales = model.readlocale()
+ for locale in locales:
+ self._store.append([locale[2], '%s (%s)' %
+ (locale[0], locale[1])])
+
+ self._treeview = gtk.TreeView(self._store)
+ self._treeview.set_search_entry(self._entry)
+ self._treeview.set_search_equal_func(self._search)
+ self._treeview.set_search_column(1)
+ self._scrolled_window.add(self._treeview)
+ self._treeview.show()
+
+ self._tvcolumn = gtk.TreeViewColumn(_('Language'))
+ self.cell = gtk.CellRendererText()
+ self._tvcolumn.pack_start(self.cell, True)
+ self._tvcolumn.add_attribute(self.cell, 'text', 1)
+ self._tvcolumn.set_sort_column_id(1)
+ self._treeview.append_column(self._tvcolumn)
+
+ for row in self._store:
+ if self._lang in row[0]:
+ self._treeview.set_cursor(row.path, self._tvcolumn, False)
+ self._treeview.scroll_to_cell(row.path, self._tvcolumn,
+ True, 0.5, 0.5)
+ break
+
+ self._treeview.connect("cursor-changed", self.__langchanged_cd)
+
+ self.pack_start(self._scrolled_window)
+ self._scrolled_window.show()
+
+ def undo(self):
+ self._model.set_language(self._lang)
+ self.restart = False
+
+ def __realize_cb(self, widget):
+ self._entry.grab_focus()
+
+ def _search(self, model, column_, key, iter_, data=None):
+ for row in model:
+ if key in row[1] or key.capitalize() in row[1]:
+ self._treeview.set_cursor(row.path, self._tvcolumn, False)
+ self._treeview.scroll_to_cell(row.path, self._tvcolumn,
+ True, 0.5, 0.5)
+ return True
+ return False
+
+ def __langchanged_cd(self, treeview, data=None):
+ row = treeview.get_selection().get_selected()
+ if self._lang_set == self._store.get_value(row[1], 0):
+ return
+
+ if self._lang_sid:
+ gobject.source_remove(self._lang_sid)
+ self._lang_sid = gobject.timeout_add(1000, self.__lang_timeout_cb,
+ self._store.get_value(row[1], 0))
+
+ def __lang_timeout_cb(self, code):
+ self._lang_sid = 0
+ self._model.set_language(code)
+ self.restart = True
+ self._lang_set = code
+ return False
diff --git a/src/controlpanel/view/network.py b/src/controlpanel/view/network.py
new file mode 100644
index 0000000..c1814dd
--- /dev/null
+++ b/src/controlpanel/view/network.py
@@ -0,0 +1,212 @@
+import gtk
+import gobject
+import gettext
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics import style
+from sugar.graphics.icon import Icon
+
+from controlpanel.detailview import DetailView
+from controlpanel.inlinealert import InlineAlert
+
+ICON = 'module-network'
+TITLE = _('Network')
+
+class Network(DetailView):
+ def __init__(self, model, alerts):
+ DetailView.__init__(self)
+
+ self.emit('valid_section', True)
+
+ self._jabber_sid = 0
+ self._jabber_valid = True
+ self._radio_valid = True
+ self.restart_alerts = alerts
+
+ self._model = model
+ self._jabber = self._model.get_jabber()
+
+ self.set_border_width(style.DEFAULT_SPACING * 2)
+ self.set_spacing(style.DEFAULT_SPACING)
+ group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL)
+
+ self._radio_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ self._jabber_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING)
+
+ separator_wireless = gtk.HSeparator()
+ self.pack_start(separator_wireless, expand=False)
+ separator_wireless.show()
+
+ label_wireless = gtk.Label(_('Wireless'))
+ label_wireless.set_alignment(0, 0)
+ self.pack_start(label_wireless, expand=False)
+ label_wireless.show()
+ box_wireless = gtk.VBox()
+ box_wireless.set_border_width(style.DEFAULT_SPACING * 2)
+ box_wireless.set_spacing(style.DEFAULT_SPACING)
+ box_radio = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label_radio = gtk.Label(_('Radio:'))
+ label_radio.set_alignment(1, 0.5)
+ label_radio.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ box_radio.pack_start(label_radio, expand=False)
+ group.add_widget(label_radio)
+ label_radio.show()
+ button = gtk.CheckButton()
+ button.set_alignment(0, 0)
+ button.connect('toggled', self.__radio_toggled_cb)
+ box_radio.pack_start(button, expand=False)
+ button.show()
+ box_wireless.pack_start(box_radio, expand=False)
+ box_radio.show()
+
+ icon_radio = Icon(icon_name='emblem-warning',
+ fill_color=style.COLOR_SELECTION_GREY.get_svg(),
+ stroke_color=style.COLOR_WHITE.get_svg())
+ self._radio_alert = InlineAlert(icon=icon_radio)
+ icon_radio.show()
+ label_radio_error = gtk.Label()
+ group.add_widget(label_radio_error)
+ self._radio_alert_box.pack_start(label_radio_error, expand=False)
+ label_radio_error.show()
+ self._radio_alert_box.pack_start(self._radio_alert, expand=False)
+ box_wireless.pack_end(self._radio_alert_box, expand=False)
+ self._radio_alert_box.show()
+ try:
+ self._radio_state = self._model.get_radio()
+ except Exception, detail:
+ self._radio_alert.props.msg = detail
+ self._radio_alert.show()
+ if 'radio' in self.restart_alerts:
+ self._radio_alert.props.msg = self._restart_msg
+ self._radio_alert.show()
+
+ self.pack_start(box_wireless, expand=False)
+ box_wireless.show()
+
+ separator_mesh = gtk.HSeparator()
+ self.pack_start(separator_mesh, False)
+ separator_mesh.show()
+
+ label_mesh = gtk.Label(_('Mesh'))
+ label_mesh.set_alignment(0, 0)
+ self.pack_start(label_mesh, expand=False)
+ label_mesh.show()
+ box_mesh = gtk.VBox()
+ box_mesh.set_border_width(style.DEFAULT_SPACING * 2)
+ box_mesh.set_spacing(style.DEFAULT_SPACING)
+
+ box_server = gtk.HBox(spacing=style.DEFAULT_SPACING)
+ label_server = gtk.Label(_('Server:'))
+ label_server.set_alignment(1, 0.5)
+ label_server.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_SELECTION_GREY.get_gdk_color())
+ box_server.pack_start(label_server, expand=False)
+ group.add_widget(label_server)
+ label_server.show()
+ self._entry = gtk.Entry()
+ self._entry.set_alignment(0)
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.set_size_request(int(gtk.gdk.screen_width() / 3), -1)
+ self._entry.set_text(self._jabber)
+ self._entry.connect('changed', self.__jabber_changed_cb)
+ box_server.pack_start(self._entry, expand=False)
+ self._entry.show()
+ box_mesh.pack_start(box_server, expand=False)
+ box_server.show()
+
+ icon_jabber = Icon(icon_name='emblem-warning',
+ fill_color=style.COLOR_SELECTION_GREY.get_svg(),
+ stroke_color=style.COLOR_WHITE.get_svg())
+ self._jabber_alert = InlineAlert(icon=icon_jabber)
+ icon_jabber.show()
+ label_jabber_error = gtk.Label()
+ group.add_widget(label_jabber_error)
+ self._jabber_alert_box.pack_start(label_jabber_error, expand=False)
+ label_jabber_error.show()
+ self._jabber_alert_box.pack_start(self._jabber_alert, expand=False)
+ box_mesh.pack_end(self._jabber_alert_box, expand=False)
+ self._jabber_alert_box.show()
+ if 'jabber' in self.restart_alerts:
+ self._jabber_alert.props.msg = self._restart_msg
+ self._jabber_alert.show()
+
+ self.pack_start(box_mesh, expand=False)
+ box_mesh.show()
+
+ def undo(self):
+ self._model.set_jabber(self._jabber)
+
+ self._entry.set_text(self._jabber)
+
+ self._jabber_valid = True
+ self._radio_valid = True
+ self.restart = False
+ self.restart_alerts = []
+
+ def __radio_toggled_cb(self, widget, data=None):
+ radio_state = ('off', 'on')[widget.get_active()]
+ try:
+ self._model.set_radio(radio_state)
+ except Exception, detail:
+ self._radio_alert.props.msg = detail
+ self._radio_valid = False
+ else:
+ self._radio_alert.props.msg = self._restart_msg
+ self._radio_valid = True
+ if radio_state != self._radio_state:
+ self.restart = True
+ self.restart_alerts.append('radio')
+ else:
+ self.restart = False
+
+ if self._radio_valid and self._jabber_valid:
+ self.emit('valid_section', True)
+ else:
+ self.emit('valid_section', False)
+
+ if not self._radio_alert.props.visible or \
+ radio_state != self._jabber:
+ self._radio_alert.show()
+ else:
+ self._radio_alert.hide()
+
+ 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(1000,
+ self.__jabber_timeout_cb, widget)
+
+ def __jabber_timeout_cb(self, widget):
+ self._jabber_sid = 0
+ try:
+ self._model.set_jabber(widget.get_text())
+ except ValueError, detail:
+ self._jabber_alert.props.msg = detail
+ self._jabber_valid = False
+ else:
+ self._jabber_alert.props.msg = self._restart_msg
+ self._jabber_valid = True
+ if widget.get_text() != self._jabber:
+ self.restart = True
+ self.restart_alerts.append('jabber')
+ else:
+ self.restart = False
+
+ if self._jabber_valid and self._radio_valid:
+ self.emit('valid_section', True)
+ else:
+ self.emit('valid_section', False)
+
+ if not self._jabber_alert.props.visible or \
+ widget.get_text() != self._jabber:
+ self._jabber_alert.show()
+ else:
+ self._jabber_alert.hide()
+
+ return False
diff --git a/src/controlpanel/view/timezone.py b/src/controlpanel/view/timezone.py
new file mode 100644
index 0000000..8c768cf
--- /dev/null
+++ b/src/controlpanel/view/timezone.py
@@ -0,0 +1,108 @@
+import gtk
+import gobject
+import gettext
+_ = lambda msg: gettext.dgettext('sugar', msg)
+
+from sugar.graphics import style
+from sugar.graphics import iconentry
+
+from controlpanel.detailview import DetailView
+
+ICON = 'module-date_and_time'
+TITLE = _('Date & Time')
+
+class Timezone(DetailView):
+ def __init__(self, model, alerts):
+ DetailView.__init__(self)
+ self._model = model
+
+ self.set_border_width(style.DEFAULT_SPACING * 2)
+ self.set_spacing(style.DEFAULT_SPACING)
+
+ self.connect("realize", self.__realize_cb)
+
+ self.restart = False
+ self._zone_sid = 0
+ self._zone = self._model.get_timezone()
+ self._zone_set = self._zone
+
+ self._entry = iconentry.IconEntry()
+ self._entry.set_icon_from_name(iconentry.ICON_ENTRY_PRIMARY,
+ 'system-search')
+ self._entry.add_clear_button()
+ self._entry.modify_bg(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self._entry.modify_base(gtk.STATE_INSENSITIVE,
+ style.COLOR_WHITE.get_gdk_color())
+ self.pack_start(self._entry, False)
+ self._entry.show()
+
+ self._scrolled_window = gtk.ScrolledWindow()
+ self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ self._scrolled_window.set_shadow_type(gtk.SHADOW_IN)
+
+ self._store = gtk.ListStore(gobject.TYPE_STRING)
+ zones = model.read_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)
+ self._treeview.set_search_column(0)
+ self._scrolled_window.add(self._treeview)
+ self._treeview.show()
+
+ self._tvcolumn = gtk.TreeViewColumn(_('Timezone'))
+ self.cell = gtk.CellRendererText()
+ self._tvcolumn.pack_start(self.cell, True)
+ self._tvcolumn.add_attribute(self.cell, 'text', 0)
+ self._tvcolumn.set_sort_column_id(0)
+ self._treeview.append_column(self._tvcolumn)
+
+ for row in self._store:
+ if self._zone == row[0]:
+ self._treeview.set_cursor(row.path, self._tvcolumn, False)
+ self._treeview.scroll_to_cell(row.path, self._tvcolumn,
+ True, 0.5, 0.5)
+ break
+
+ self._treeview.connect("cursor-changed", self.__zonechanged_cd)
+
+ self.pack_start(self._scrolled_window)
+ self._scrolled_window.show()
+
+ def undo(self):
+ self._model.set_timezone(self._zone)
+ self.restart = False
+
+ def __realize_cb(self, widget):
+ self._entry.grab_focus()
+
+ def _search(self, model, column_, key, iter_, data=None):
+ for row in model:
+ if key in row[0] or key.capitalize() in row[0]:
+ self._treeview.set_cursor(row.path, self._tvcolumn, False)
+ self._treeview.scroll_to_cell(row.path, self._tvcolumn,
+ True, 0.5, 0.5)
+ return True
+ return False
+
+ def __zonechanged_cd(self, treeview, data=None):
+ list_, row = treeview.get_selection().get_selected()
+ if not row:
+ row = self._zone_set
+ if self._zone_set == self._store.get_value(row, 0):
+ return False
+
+ if self._zone_sid:
+ gobject.source_remove(self._zone_sid)
+ self._zone_sid = gobject.timeout_add(1000, self.__lang_timeout_cb, row)
+ return True
+
+ def __lang_timeout_cb(self, row):
+ self._zone_sid = 0
+ self._model.set_timezone(self._store.get_value(row, 0))
+ self.restart = True
+ self._zone_set = row
+ return False
diff --git a/src/view/home/activitiesring.py b/src/view/home/activitiesring.py
index 6a4b22d..73b64e1 100644
--- a/src/view/home/activitiesring.py
+++ b/src/view/home/activitiesring.py
@@ -43,6 +43,7 @@ from view.home.MyIcon import MyIcon
from model import shellmodel
from model.shellmodel import ShellModel
from hardware import schoolserver
+from controlpanel.gui import ControlPanel
_logger = logging.getLogger('ActivitiesRing')
@@ -290,14 +291,14 @@ class _MyIcon(MyIcon):
#secondary_text='Sample secondary label',
icon=palette_icon)
- item = MenuItem(_('About this XO'))
+ item = MenuItem(_('Control Panel'))
icon = Icon(icon_name='computer-xo', icon_size=gtk.ICON_SIZE_MENU,
xo_color=self._profile.color)
item.set_image(icon)
icon.show()
- item.connect('activate', self._about_activate_cb)
+ item.connect('activate', self.__controlpanel_activate_cb)
palette.menu.append(item)
item.show()
@@ -352,62 +353,13 @@ class _MyIcon(MyIcon):
if self._profile.is_registered():
self.get_palette().menu.remove(menuitem)
- def _about_activate_cb(self, menuitem):
- dialog = gtk.Dialog(_('About this XO'),
- self.palette,
- gtk.DIALOG_MODAL |
- gtk.DIALOG_DESTROY_WITH_PARENT,
- (gtk.STOCK_OK, gtk.RESPONSE_OK))
-
- not_available = _('Not available')
- build = self._read_file('/boot/olpc_build')
- if build is None:
- build = not_available
- label_build = gtk.Label('Build: %s' % build)
- label_build.set_alignment(0, 0.5)
- label_build.show()
- vbox = dialog.get_child()
- vbox.pack_start(label_build)
-
- firmware = self._read_file('/ofw/openprom/model')
- if firmware is None:
- firmware = not_available
- else:
- firmware = re.split(" +", firmware)
- if len(firmware) == 3:
- firmware = firmware[1]
- label_firmware = gtk.Label('Firmware: %s' % firmware)
- label_firmware.set_alignment(0, 0.5)
- label_firmware.show()
- vbox.pack_start(label_firmware)
-
- serial = self._read_file('/ofw/serial-number')
- if serial is None:
- serial = not_available
- label_serial = gtk.Label('Serial Number: %s' % serial)
- label_serial.set_alignment(0, 0.5)
- label_serial.show()
- vbox.pack_start(label_serial)
-
- dialog.set_default_response(gtk.RESPONSE_OK)
- dialog.connect('response', self._response_cb)
- dialog.show()
-
- def _read_file(self, path):
- if os.access(path, os.R_OK) == 0:
- _logger.error('read_file() No such file or directory: %s' % path)
- return None
-
- fd = open(path, 'r')
- value = fd.read()
- fd.close()
- if value:
- value = value.strip('\n')
- return value
- else:
- _logger.error('read_file() No information in file or directory: %s'
- % path)
- return None
+ def get_toplevel(self):
+ return hippo.get_canvas_for_item(self).get_toplevel()
+
+ def __controlpanel_activate_cb(self, menuitem):
+ panel = ControlPanel()
+ panel.set_transient_for(self.get_toplevel())
+ panel.show()
def _response_cb(self, widget, response_id):
if response_id == gtk.RESPONSE_OK: