diff --git a/bin/sugar-control-panel.in b/bin/sugar-control-panel.in
index 922f95c..e70764b 100644
--- a/bin/sugar-control-panel.in
+++ b/bin/sugar-control-panel.in
@@ -17,10 +17,12 @@
import sys
-sys.path.insert(0, '@prefix@/share/sugar/shell')
+path = '@prefix@/share/sugar/shell'
+
+sys.path.insert(0, path)
from controlpanel.cmd import main
-main()
+main(path)
diff --git a/configure.ac b/configure.ac
index a99fdf0..1981fbc 100644
--- a/configure.ac
+++ b/configure.ac
@@ -43,6 +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 f89132c..e626af4 100644
--- a/src/controlpanel/Makefile.am
+++ b/src/controlpanel/Makefile.am
@@ -1,5 +1,10 @@
+SUBDIRS = model view icons
+
sugardir = $(pkgdatadir)/shell/controlpanel
sugar_PYTHON = \
__init__.py \
cmd.py \
- control.py
+ gui.py \
+ controltoolbar.py \
+ detailview.py \
+ inlinealert.py
diff --git a/src/controlpanel/cmd.py b/src/controlpanel/cmd.py
index 24653b2..db23559 100644
--- a/src/controlpanel/cmd.py
+++ b/src/controlpanel/cmd.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2007, One Laptop Per Child
+# 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
@@ -16,11 +16,12 @@
import sys
import getopt
+import os
from gettext import gettext as _
-from controlpanel import control
-
+
def cmd_help():
+ '''Print the help to the screen'''
print _('Usage: sugar-control-panel [ option ] key [ args ... ] \n\
Control for the sugar environment. \n\
Options: \n\
@@ -31,7 +32,28 @@ def cmd_help():
-s key set the current value for the key \n\
')
-def main():
+def note_restart():
+ '''Instructions how to restart sugar'''
+ print _('To apply your changes you have to restart sugar.\n' +
+ 'Hit ctrl+alt+erase on the keyboard to trigger a restart.')
+
+def load_modules(path):
+ '''Build a list of pointers to available modules in the model directory
+ and load them.
+ '''
+ subpath = ['controlpanel', 'model']
+ names = os.listdir(os.path.join(path, '/'.join(subpath)))
+
+ modules = []
+ for name in names:
+ if name.endswith('.py') and name != '__init__.py':
+ tmp = name.strip('.py')
+ mod = __import__('.'.join(subpath) + '.' +
+ tmp, globals(), locals(), [tmp])
+ modules.append(mod)
+ return modules
+
+def main(path):
try:
opts, args = getopt.getopt(sys.argv[1:], "h:s:g:l", [])
except getopt.GetoptError:
@@ -41,37 +63,49 @@ def main():
if not opts:
cmd_help()
sys.exit()
-
+
+ modules = load_modules(path)
+
for opt, key in opts:
- if opt in ("-h"):
- method = getattr(control, 'set_' + key, None)
- if method is None:
- print _("sugar-control-panel: key=%s not an available option"
- % key)
- sys.exit()
- else:
- print method.__doc__
- if opt in ("-l"):
- elems = dir(control)
- for elem in elems:
- if elem.startswith('set_'):
- print elem[4:]
+ if opt in ("-h"):
+ for module in modules:
+ method = getattr(module, 'set_' + key, None)
+ if method:
+ print method.__doc__
+ sys.exit()
+ print _("sugar-control-panel: key=%s not an available option"
+ % key)
+ if opt in ("-l"):
+ for module in modules:
+ methods = dir(module)
+ print '%s:' % module.__name__.split('.')[-1]
+ for method in methods:
+ if method.startswith('get_'):
+ print ' %s' % method[4:]
if opt in ("-g"):
- method = getattr(control, 'print_' + key, None)
- if method is None:
- print _("sugar-control-panel: key=%s not an available option"
- % key)
- sys.exit()
- else:
- method()
+ for module in modules:
+ method = getattr(module, 'print_' + key, None)
+ if method:
+ try:
+ method()
+ except Exception, detail:
+ print _("sugar-control-panel: %s"
+ % detail)
+ sys.exit()
+ print _("sugar-control-panel: key=%s not an available option"
+ % key)
if opt in ("-s"):
- method = getattr(control, 'set_' + key, None)
- if method is None:
- print _("sugar-control-panel: key=%s not an available option"
- % key)
- sys.exit()
- else:
- try:
- method(*args)
- except Exception, e:
- print _("sugar-control-panel: %s"% e)
+ for module in modules:
+ method = getattr(module, 'set_' + key, None)
+ if method:
+ note = 0
+ try:
+ note = method(*args)
+ except Exception, detail:
+ print _("sugar-control-panel: %s"
+ % detail)
+ if note == 'RESTART':
+ note_restart()
+ sys.exit()
+ print _("sugar-control-panel: key=%s not an available option"
+ % key)
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 @@
+
+
+]>
\ 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 @@
+
+
+]>
\ 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 @@
+
+
+]>
\ 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 @@
+
+
+]>
\ 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 @@
+
+
+]>
\ 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/model/Makefile.am b/src/controlpanel/model/Makefile.am
new file mode 100644
index 0000000..4bc3264
--- /dev/null
+++ b/src/controlpanel/model/Makefile.am
@@ -0,0 +1,8 @@
+sugardir = $(pkgdatadir)/shell/controlpanel/model
+sugar_PYTHON = \
+ __init__.py \
+ network.py \
+ aboutme.py \
+ language.py \
+ timezone.py \
+ aboutxo.py
diff --git a/src/controlpanel/model/__init__.py b/src/controlpanel/model/__init__.py
new file mode 100644
index 0000000..2b0f269
--- /dev/null
+++ b/src/controlpanel/model/__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/model/aboutme.py b/src/controlpanel/model/aboutme.py
new file mode 100644
index 0000000..98726c6
--- /dev/null
+++ b/src/controlpanel/model/aboutme.py
@@ -0,0 +1,108 @@
+# 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
+#
+
+from gettext import gettext as _
+
+from sugar import profile
+from sugar.graphics.xocolor import XoColor
+
+_COLORS = {'red': {'dark':'#b20008', 'medium':'#e6000a', 'light':'#ffadce'},
+ 'orange': {'dark':'#9a5200', 'medium':'#c97e00', 'light':'#ffc169'},
+ 'yellow': {'dark':'#807500', 'medium':'#be9e00', 'light':'#fffa00'},
+ 'green': {'dark':'#008009', 'medium':'#00b20d', 'light':'#8bff7a'},
+ 'blue': {'dark':'#00588c', 'medium':'#005fe4', 'light':'#bccdff'},
+ 'purple': {'dark':'#5e008c', 'medium':'#7f00bf', 'light':'#d1a3ff'}
+ }
+
+_MODIFIERS = ('dark', 'medium', 'light')
+
+def get_nick():
+ return profile.get_nick_name()
+
+def print_nick():
+ print get_nick()
+
+def set_nick(nick):
+ """Set the nickname.
+ nick : e.g. 'walter'
+ """
+ if not nick:
+ raise ValueError(_("You must enter a name."))
+ pro = profile.get_profile()
+ pro.nick_name = nick
+ pro.save()
+ return 'RESTART'
+
+def get_color():
+ return profile.get_color()
+
+def print_color():
+ color = get_color().to_string()
+ tmp = color.split(',')
+
+ stroke = None
+ fill = None
+ for color in _COLORS:
+ for hue in _COLORS[color]:
+ if _COLORS[color][hue] == tmp[0]:
+ stroke = (color, hue)
+ if _COLORS[color][hue] == tmp[1]:
+ fill = (color, hue)
+
+ if stroke is not None:
+ print 'stroke: color=%s hue=%s' % (stroke[0], stroke[1])
+ else:
+ print 'stroke: %s' % (tmp[0])
+ if fill is not None:
+ print 'fill: color=%s hue=%s' % (fill[0], fill[1])
+ else:
+ print 'fill: %s' % (tmp[1])
+
+def set_color(stroke, fill, modstroke='medium', modfill='medium'):
+ """Set the system color by setting a fill and stroke color.
+ fill : [red, orange, yellow, blue, purple]
+ stroke : [red, orange, yellow, blue, purple]
+ hue stroke : [dark, medium, light] (optional)
+ hue fill : [dark, medium, light] (optional)
+ """
+
+ if modstroke not in _MODIFIERS or modfill not in _MODIFIERS:
+ print (_("Error in specified color modifiers."))
+ return
+ if stroke not in _COLORS or fill not in _COLORS:
+ print (_("Error in specified colors."))
+ return
+
+ if modstroke == modfill:
+ if modfill == 'medium':
+ modfill = 'light'
+ else:
+ modfill = 'medium'
+
+ color = _COLORS[stroke][modstroke] + ',' + _COLORS[fill][modfill]
+ pro = profile.get_profile()
+ pro.color = XoColor(color)
+ pro.save()
+ return "RESTART"
+
+def set_color_xo(color):
+ """Set a color with an XoColor
+ """
+ pro = profile.get_profile()
+ pro.color = color
+ pro.save()
+ return "RESTART"
diff --git a/src/controlpanel/model/aboutxo.py b/src/controlpanel/model/aboutxo.py
new file mode 100644
index 0000000..1a63f32
--- /dev/null
+++ b/src/controlpanel/model/aboutxo.py
@@ -0,0 +1,22 @@
+# 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
+#
+
+def get_aboutxo():
+ return 'TODO: make info available here as well for the cml interface'
+
+def print_aboutxo():
+ print get_aboutxo()
diff --git a/src/controlpanel/model/language.py b/src/controlpanel/model/language.py
new file mode 100644
index 0000000..49bac07
--- /dev/null
+++ b/src/controlpanel/model/language.py
@@ -0,0 +1,123 @@
+# 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
+#
+#
+# The language config is based on the system-config-language
+# (http://fedoraproject.org/wiki/SystemConfig/language) tool
+# Parts of the code were reused.
+#
+
+import os
+from gettext import gettext as _
+import subprocess
+
+def readlocale():
+ fdp = subprocess.Popen(['locale', '-av'], stdout=subprocess.PIPE)
+ lines = fdp.stdout.read().split('\n')
+ locales = []
+ try:
+ for line in lines:
+ if line.find('locale:') != -1:
+ loc = line.lstrip('locale: ')
+ loc = loc.split('archive:')[0].strip()
+ elif line.find('language |') != -1:
+ lang = line.lstrip('language |')
+ elif line.find('territory |') != -1:
+ ter = line.lstrip('territory |')
+ if loc.endswith('utf8') and len(lang):
+ locales.append((lang, ter, loc))
+ except Exception, error:
+ print "Error reading locale: %s" % error
+ locales.sort()
+ return locales
+
+def _initialize():
+ languages = readlocale()
+ set_language.__doc__ += '\n'
+ for lang in languages:
+ set_language.__doc__ += '%s \n' % (lang[0].replace(' ', '_') + '/' +
+ lang[1].replace(' ', '_'))
+
+def _write_i18n(lang):
+ path = os.path.join(os.environ.get("HOME"), '.i18n')
+ if os.access(path, os.W_OK) == 0:
+ print(_("Could not access %s. Create standard settings.") % path)
+ fd = open(path, 'w')
+ fd.write('LANG="en_US.utf8"\n')
+ fd.close()
+ else:
+ fd = open(path, 'r')
+ lines = fd.readlines()
+ fd.close()
+ for i in range(len(lines)):
+ if lines[i][:5] == "LANG=":
+ lines[i] = 'LANG="' + lang + '"\n'
+ fd = open(path, 'w')
+ fd.writelines(lines)
+ fd.close()
+
+def get_language():
+ path = os.path.join(os.environ.get("HOME"), '.i18n')
+ if os.access(path, os.R_OK) == 0:
+ print(_("Could not access %s. Create standard settings.") % path)
+ fd = open(path, 'w')
+ default = 'en_US.utf8'
+ fd.write('LANG="%s"\n' % default)
+ fd.close()
+ return default
+
+ fd = open(path, "r")
+ lines = fd.readlines()
+ fd.close()
+
+ lang = None
+
+ for line in lines:
+ if line[:5] == "LANG=":
+ lang = line[5:].replace('"', '')
+ lang = lang.strip()
+
+ return lang
+
+def print_language():
+ code = get_language()
+
+ languages = readlocale()
+ for lang in languages:
+ if lang[2] == code:
+ print lang[0].replace(' ', '_') + '/' + lang[1].replace(' ', '_')
+ return
+ print (_("Language for code=%s could not be determined.") % code)
+
+def set_language(language):
+ """Set the system language.
+ languages :
+ """
+ if language.endswith('utf8'):
+ _write_i18n(language)
+ return "RESTART"
+ else:
+ languages = readlocale()
+ for lang in languages:
+ code = lang[0].replace(' ', '_') + '/' + lang[1].replace(' ', '_')
+ if code == language:
+ _write_i18n(lang[2])
+ return "RESTART"
+ print (_("Sorry I do not speak \'%s\'.") % language)
+
+# inilialize the docstrings for the language
+_initialize()
+
diff --git a/src/controlpanel/model/network.py b/src/controlpanel/model/network.py
new file mode 100644
index 0000000..6190a4d
--- /dev/null
+++ b/src/controlpanel/model/network.py
@@ -0,0 +1,86 @@
+# 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 dbus
+from gettext import gettext as _
+
+from sugar import profile
+
+NM_SERVICE_NAME = 'org.freedesktop.NetworkManager'
+NM_SERVICE_PATH = '/org/freedesktop/NetworkManager'
+NM_SERVICE_IFACE = 'org.freedesktop.NetworkManager'
+NM_ASLEEP = 1
+
+KEYWORDS = ['network', 'jabber', 'radio', 'server']
+
+class ReadError(Exception):
+ def __init__(self, value):
+ self.value = value
+ def __str__(self):
+ return repr(self.value)
+
+def get_jabber():
+ pro = profile.get_profile()
+ return pro.jabber_server
+
+def print_jabber():
+ print get_jabber()
+
+def set_jabber(server):
+ """Set the jabber server
+ server : e.g. 'olpc.collabora.co.uk'
+ """
+ if not server:
+ raise ValueError(_("You must enter a server."))
+ pro = profile.get_profile()
+ pro.jabber_server = server
+ pro.jabber_registered = False
+ pro.save()
+ return "RESTART"
+
+def get_radio():
+ bus = dbus.SystemBus()
+ proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
+ nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
+ state = nm.getWirelessEnabled()
+ if state == 0:
+ return _('off')
+ elif state == 1:
+ return _('on')
+ else:
+ raise ReadError(_('State is unknown.'))
+
+def print_radio():
+ print get_radio()
+
+def set_radio(state):
+ """Turn Radio 'on' or 'off'
+ state : 'on/off'
+ """
+ if state == 'on':
+ bus = dbus.SystemBus()
+ proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
+ nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
+ nm.setWirelessEnabled(True)
+ elif state == 'off':
+ bus = dbus.SystemBus()
+ proxy = bus.get_object(NM_SERVICE_NAME, NM_SERVICE_PATH)
+ nm = dbus.Interface(proxy, NM_SERVICE_IFACE)
+ nm.setWirelessEnabled(False)
+ else:
+ raise ValueError(_("Error in specified radio argument use on/off."))
+
diff --git a/src/controlpanel/model/timezone.py b/src/controlpanel/model/timezone.py
new file mode 100644
index 0000000..5ebb9df
--- /dev/null
+++ b/src/controlpanel/model/timezone.py
@@ -0,0 +1,80 @@
+# 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
+#
+#
+# The timezone config is based on the system-config-date
+# (http://fedoraproject.org/wiki/SystemConfig/date) tool.
+# Parts of the code were reused.
+#
+
+import os
+from gettext import gettext as _
+
+from sugar import profile
+
+def _initialize():
+ '''Initialize the docstring of the set function'''
+ timezones = read_timezones()
+ for timezone in timezones:
+ set_timezone.__doc__ += timezone + '\n'
+
+def read_timezones(fn='/usr/share/zoneinfo/zone.tab'):
+ fd = open (fn, 'r')
+ lines = fd.readlines()
+ fd.close()
+ timezones = []
+ for line in lines:
+ if line.startswith('#'):
+ continue
+ line = line.split()
+ if len(line) > 1:
+ timezones.append(line[2])
+ timezones.sort()
+
+ for offset in xrange(-14, 13):
+ if offset < 0:
+ tz = 'GMT%d' % offset
+ elif offset > 0:
+ tz = 'GMT+%d' % offset
+ else:
+ tz = 'GMT'
+ timezones.append(tz)
+ return timezones
+
+def get_timezone():
+ pro = profile.get_profile()
+ return pro.timezone
+
+def print_timezone():
+ print get_timezone()
+
+def set_timezone(timezone):
+ """Set the system timezone
+ server : e.g. 'America/Los_Angeles'
+ """
+ timezones = read_timezones()
+ if timezone in timezones:
+ os.environ['TZ'] = timezone
+ pro = profile.get_profile()
+ pro.timezone = timezone
+ pro.save()
+ else:
+ raise ValueError(_("Error timezone does not exist."))
+ return 'RESTART'
+
+# inilialize the docstrings for the timezone
+_initialize()
+
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/main.py b/src/main.py
index 76d753f..3a88a37 100644
--- a/src/main.py
+++ b/src/main.py
@@ -110,6 +110,9 @@ def main():
win.show_all()
gtk.main()
+ # set timezone
+ os.environ['TZ'] = get_profile().timezone
+
if os.environ.has_key("SUGAR_TP_DEBUG"):
# Allow the user time to start up telepathy connection managers
# using the Sugar DBus bus address
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: