From 091adf5ee3ae797507328ab72225133039dfdebb Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Sun, 28 Sep 2008 10:27:47 +0000 Subject: Move the shell code into site-packages. --- (limited to 'src/jarabe/controlpanel') diff --git a/src/jarabe/controlpanel/Makefile.am b/src/jarabe/controlpanel/Makefile.am new file mode 100644 index 0000000..7d17b4f --- /dev/null +++ b/src/jarabe/controlpanel/Makefile.am @@ -0,0 +1,12 @@ +SUBDIRS = aboutme aboutxo datetime frame language network power + +sugardir = $(pythondir)/jarabe/controlpanel +sugar_PYTHON = \ + __init__.py \ + cmd.py \ + gui.py \ + inlinealert.py \ + sectionview.py \ + toolbar.py + + diff --git a/src/jarabe/controlpanel/__init__.py b/src/jarabe/controlpanel/__init__.py new file mode 100644 index 0000000..a9dd95a --- /dev/null +++ b/src/jarabe/controlpanel/__init__.py @@ -0,0 +1,16 @@ +# Copyright (C) 2006-2007, Red Hat, Inc. +# +# 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/jarabe/controlpanel/aboutme/Makefile.am b/src/jarabe/controlpanel/aboutme/Makefile.am new file mode 100644 index 0000000..da77593 --- /dev/null +++ b/src/jarabe/controlpanel/aboutme/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/aboutme + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/aboutme/__init__.py b/src/jarabe/controlpanel/aboutme/__init__.py new file mode 100644 index 0000000..b683e28 --- /dev/null +++ b/src/jarabe/controlpanel/aboutme/__init__.py @@ -0,0 +1,25 @@ +# 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 + +from gettext import gettext as _ +from sugar import profile + +CLASS = 'AboutMe' +ICON = 'module-about_me' +TITLE = _('About Me') +COLOR = profile.get_color() + + diff --git a/src/jarabe/controlpanel/aboutme/model.py b/src/jarabe/controlpanel/aboutme/model.py new file mode 100644 index 0000000..3818792 --- /dev/null +++ b/src/jarabe/controlpanel/aboutme/model.py @@ -0,0 +1,116 @@ +# 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() + if not isinstance(nick, unicode): + nick = unicode(nick, 'utf-8') + pro.nick_name = nick + pro.save() + return 1 + +def get_color(): + return profile.get_color() + +def print_color(): + color_string = get_color().to_string() + tmp = color_string.split(',') + + stroke_tuple = None + fill_tuple = None + for color in _COLORS: + for hue in _COLORS[color]: + if _COLORS[color][hue] == tmp[0]: + stroke_tuple = (color, hue) + if _COLORS[color][hue] == tmp[1]: + fill_tuple = (color, hue) + + if stroke_tuple is not None: + print _('stroke: color=%s hue=%s') % (stroke_tuple[0], + stroke_tuple[1]) + else: + print _('stroke: %s') % (tmp[0]) + if fill_tuple is not None: + print _('fill: color=%s hue=%s') % (fill_tuple[0], fill_tuple[1]) + else: + print _('fill: %s') % (tmp[1]) + +def set_color(stroke, fill, stroke_modifier='medium', fill_modifier='medium'): + """Set the system color by setting a fill and stroke color. + fill : [red, orange, yellow, blue, green, purple] + stroke : [red, orange, yellow, blue, green, purple] + hue stroke : [dark, medium, light] (optional) + hue fill : [dark, medium, light] (optional) + """ + + if stroke_modifier not in _MODIFIERS or fill_modifier 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 stroke_modifier == fill_modifier: + if fill_modifier == 'medium': + fill_modifier = 'light' + else: + fill_modifier = 'medium' + + color = _COLORS[stroke][stroke_modifier] + ',' \ + + _COLORS[fill][fill_modifier] + pro = profile.get_profile() + pro.color = XoColor(color) + pro.save() + return 1 + +def get_color_xo(): + return profile.get_color() + +def set_color_xo(color): + """Set a color with an XoColor + This method is used by the graphical user interface + """ + pro = profile.get_profile() + pro.color = color + pro.save() + return 1 diff --git a/src/jarabe/controlpanel/aboutme/view.py b/src/jarabe/controlpanel/aboutme/view.py new file mode 100644 index 0000000..fc4f351 --- /dev/null +++ b/src/jarabe/controlpanel/aboutme/view.py @@ -0,0 +1,226 @@ +# 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 gobject +from gettext import gettext as _ + +from sugar.graphics.icon import Icon +from sugar.graphics import style +from sugar.graphics.xocolor import XoColor +from sugar import profile + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +CLASS = 'AboutMe' +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=None): + 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(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + self._nick_sid = 0 + self._color_valid = True + self._nick_valid = True + self._color_change_handler = None + self._nick_change_handler = None + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + self._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._nick_entry = None + self._nick_alert = None + self._setup_nick() + + self._color_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._color_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._color_picker = None + self._color_alert = None + self._setup_color() + + self.setup() + + def _setup_nick(self): + label_entry = gtk.Label(_('Name:')) + label_entry.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + self._group.add_widget(label_entry) + label_entry.set_alignment(1, 0.5) + self._nick_box.pack_start(label_entry, expand=False) + label_entry.show() + + self._nick_entry = gtk.Entry() + self._nick_entry.modify_bg(gtk.STATE_INSENSITIVE, + style.COLOR_WHITE.get_gdk_color()) + self._nick_entry.modify_base(gtk.STATE_INSENSITIVE, + style.COLOR_WHITE.get_gdk_color()) + self._nick_entry.set_width_chars(25) + self._nick_box.pack_start(self._nick_entry, expand=False) + self._nick_entry.show() + + label_entry_error = gtk.Label() + self._group.add_widget(label_entry_error) + self._nick_alert_box.pack_start(label_entry_error, expand=False) + label_entry_error.show() + + self._nick_alert = InlineAlert() + 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.pack_start(self._nick_box, False) + self.pack_start(self._nick_alert_box, False) + self._nick_box.show() + self._nick_alert_box.show() + + def _setup_color(self): + label_color = gtk.Label(_('Click to change your color:')) + label_color.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + self._group.add_widget(label_color) + self._color_box.pack_start(label_color, expand=False) + label_color.show() + + self._color_picker = ColorPicker() + self._color_box.pack_start(self._color_picker, expand=False) + self._color_picker.show() + + label_color_error = gtk.Label() + self._group.add_widget(label_color_error) + self._color_alert_box.pack_start(label_color_error, expand=False) + label_color_error.show() + + self._color_alert = InlineAlert() + 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.pack_start(self._color_box, False) + self.pack_start(self._color_alert_box, False) + self._color_box.show() + self._color_alert_box.show() + + def setup(self): + self._nick_entry.set_text(self._model.get_nick()) + self._color_picker.icon.props.xo_color = self._model.get_color_xo() + + self._color_valid = True + self._nick_valid = True + self.needs_restart = False + self._nick_change_handler = self._nick_entry.connect( \ + 'changed', self.__nick_changed_cb) + self._color_change_handler = self._color_picker.connect( \ + 'color-changed', self.__color_changed_cb) + + def undo(self): + self._color_picker.disconnect(self._color_change_handler) + self._nick_entry.disconnect(self._nick_change_handler) + self._model.undo() + self._nick_alert.hide() + self._color_alert.hide() + + def _validate(self): + if self._nick_valid and self._color_valid: + self.props.is_valid = True + else: + self.props.is_valid = False + + def __nick_changed_cb(self, widget, data=None): + if self._nick_sid: + gobject.source_remove(self._nick_sid) + self._nick_sid = gobject.timeout_add(self._APPLY_TIMEOUT, + self.__nick_timeout_cb, widget) + + def __nick_timeout_cb(self, widget): + self._nick_sid = 0 + + if widget.get_text() == self._model.get_nick(): + return False + 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 + self.needs_restart = True + self.restart_alerts.append('nick') + + self._validate() + self._nick_alert.show() + return False + + def __color_changed_cb(self, colorpicker, xocolor): + self._model.set_color_xo(xocolor) + self.needs_restart = True + self._color_alert.props.msg = self.restart_msg + self._color_valid = True + self.restart_alerts.append('color') + + self._validate() + self._color_alert.show() + + + + + + diff --git a/src/jarabe/controlpanel/aboutxo/Makefile.am b/src/jarabe/controlpanel/aboutxo/Makefile.am new file mode 100644 index 0000000..77cb5d8 --- /dev/null +++ b/src/jarabe/controlpanel/aboutxo/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/aboutxo + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/aboutxo/__init__.py b/src/jarabe/controlpanel/aboutxo/__init__.py new file mode 100644 index 0000000..4dc2cb2 --- /dev/null +++ b/src/jarabe/controlpanel/aboutxo/__init__.py @@ -0,0 +1,22 @@ +# 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 + +from gettext import gettext as _ + +CLASS = 'AboutXO' +ICON = 'module-about_my_xo' +TITLE = _('About my XO') + diff --git a/src/jarabe/controlpanel/aboutxo/model.py b/src/jarabe/controlpanel/aboutxo/model.py new file mode 100644 index 0000000..ebbcca7 --- /dev/null +++ b/src/jarabe/controlpanel/aboutxo/model.py @@ -0,0 +1,101 @@ +# 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 os +import logging +import re +from gettext import gettext as _ + +_logger = logging.getLogger('ControlPanel - AboutXO') +_not_available = _('Not available') + +def get_aboutxo(): + msg = 'Serial Number: %s \nBuild Number: %s \nFirmware Number: %s \n' \ + % (get_serial_number(), get_build_number(), get_firmware_number()) + return msg + +def print_aboutxo(): + print get_aboutxo() + +def get_serial_number(): + serial_no = _read_file('/ofw/serial-number') + if serial_no is None: + serial_no = _not_available + return serial_no + +def print_serial_number(): + print get_serial_number() + +def get_build_number(): + build_no = _read_file('/boot/olpc_build') + if build_no is None: + build_no = _not_available + return build_no + +def print_build_number(): + print get_build_number() + +def get_firmware_number(): + firmware_no = _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] + return firmware_no + +def print_firmware_number(): + print get_firmware_number() + +def _read_file(path): + if os.access(path, os.R_OK) == 0: + return None + + fd = open(path, 'r') + value = fd.read() + fd.close() + if value: + value = value.strip('\n') + return value + else: + _logger.debug('No information in file or directory: %s' % path) + return None + +def get_license(): + license_file = "/usr/share/licenses/common-licenses/GPLv2" + lang = os.environ['LANG'] + if lang.endswith("UTF-8"): + lang = lang[:-6] + + try_file = license_file + "." + lang + if os.path.isfile(try_file): + license_file = try_file + else: + try_file = license_file + "." + lang.split("_")[0] + if os.path.isfile(try_file): + license_file = try_file + + try: + fd = open(license_file) + # remove 0x0c page breaks which can't be rendered in text views + license_text = fd.read().replace('\x0c', '') + fd.close() + except IOError: + license_text = _not_available + return license_text + diff --git a/src/jarabe/controlpanel/aboutxo/view.py b/src/jarabe/controlpanel/aboutxo/view.py new file mode 100644 index 0000000..e18d38d --- /dev/null +++ b/src/jarabe/controlpanel/aboutxo/view.py @@ -0,0 +1,193 @@ +# coding=utf-8 +# 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 +from gettext import gettext as _ + +import config +from sugar.graphics import style + +from jarabe.controlpanel.sectionview import SectionView + +class AboutXO(SectionView): + def __init__(self, model, alerts=None): + SectionView.__init__(self) + + self._model = model + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + + self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + + scrollwindow = gtk.ScrolledWindow() + scrollwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + self.pack_start(scrollwindow, expand=True) + scrollwindow.show() + + self._vbox = gtk.VBox() + scrollwindow.add_with_viewport(self._vbox) + self._vbox.show() + + self._setup_identity() + self._setup_software() + self._setup_copyright() + + def _setup_identity(self): + separator_identity = gtk.HSeparator() + self._vbox.pack_start(separator_identity, expand=False) + separator_identity.show() + + label_identity = gtk.Label(_('Identity')) + label_identity.set_alignment(0, 0) + self._vbox.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) + self._group.add_widget(label_serial) + label_serial.show() + label_serial_no = gtk.Label(self._model.get_serial_number()) + 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._vbox.pack_start(vbox_identity, expand=False) + vbox_identity.show() + + + def _setup_software(self): + separator_software = gtk.HSeparator() + self._vbox.pack_start(separator_software, expand=False) + separator_software.show() + + label_software = gtk.Label(_('Software')) + label_software.set_alignment(0, 0) + self._vbox.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) + self._group.add_widget(label_build) + label_build.show() + label_build_no = gtk.Label(self._model.get_build_number()) + 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_sugar = gtk.HBox(spacing=style.DEFAULT_SPACING) + label_sugar = gtk.Label(_('Sugar:')) + label_sugar.set_alignment(1, 0) + label_sugar.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + box_sugar.pack_start(label_sugar, expand=False) + self._group.add_widget(label_sugar) + label_sugar.show() + label_sugar_ver = gtk.Label(config.version) + label_sugar_ver.set_alignment(0, 0) + box_sugar.pack_start(label_sugar_ver, expand=False) + label_sugar_ver.show() + box_software.pack_start(box_sugar, expand=False) + box_sugar.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) + self._group.add_widget(label_firmware) + label_firmware.show() + label_firmware_no = gtk.Label(self._model.get_firmware_number()) + 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._vbox.pack_start(box_software, expand=False) + box_software.show() + + def _setup_copyright(self): + separator_copyright = gtk.HSeparator() + self._vbox.pack_start(separator_copyright, expand=False) + separator_copyright.show() + + label_copyright = gtk.Label(_('Copyright and License')) + label_copyright.set_alignment(0, 0) + self._vbox.pack_start(label_copyright, expand=False) + label_copyright.show() + vbox_copyright = gtk.VBox() + vbox_copyright.set_border_width(style.DEFAULT_SPACING * 2) + vbox_copyright.set_spacing(style.DEFAULT_SPACING) + + label_copyright = gtk.Label(_("© 2008 One Laptop per Child " + "Association Inc; Red Hat Inc; " + "and Contributors.")) + label_copyright.set_alignment(0, 0) + label_copyright.show() + vbox_copyright.pack_start(label_copyright, expand=False) + + label_info = gtk.Label(_("Sugar is the graphical user interface that " + "you are looking at. Sugar is free software, " + "covered by the GNU General Public License, " + "and you are welcome to change it and/or " + "distribute copies of it under certain " + "conditions described therein.")) + label_info.set_alignment(0, 0) + label_info.set_line_wrap(True) + label_info.set_size_request(gtk.gdk.screen_width() / 2, -1) + label_info.show() + vbox_copyright.pack_start(label_info, expand=False) + + expander = gtk.Expander(_("Full license:")) + expander.connect("notify::expanded", self.license_expander_cb) + expander.show() + vbox_copyright.pack_start(expander, expand=True) + + self._vbox.pack_start(vbox_copyright, expand=True) + vbox_copyright.show() + + def license_expander_cb(self, expander, param_spec): + # load/destroy the license viewer on-demand, to avoid storing the + # GPL in memory at all times + if expander.get_expanded(): + view_license = gtk.TextView() + view_license.set_editable(False) + view_license.get_buffer().set_text(self._model.get_license()) + view_license.show() + expander.add(view_license) + else: + expander.get_child().destroy() diff --git a/src/jarabe/controlpanel/cmd.py b/src/jarabe/controlpanel/cmd.py new file mode 100644 index 0000000..8e41af5 --- /dev/null +++ b/src/jarabe/controlpanel/cmd.py @@ -0,0 +1,148 @@ +# 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 sys +import getopt +import os +from gettext import gettext as _ + +import config + +_RESTART = 1 + +_same_option_warning = _("sugar-control-panel: WARNING, found more than" + " one option with the same name: %s module: %r") +_no_option_error = _("sugar-control-panel: key=%s not an available option") +_general_error = _("sugar-control-panel: %s") + +def cmd_help(): + '''Print the help to the screen''' + # TRANS: Translators, there's a empty line at the end of this string, + # which must appear in the translated string (msgstr) as well. + print _('Usage: sugar-control-panel [ option ] key [ args ... ] \n\ + Control for the sugar environment. \n\ + Options: \n\ + -h show this help message and exit \n\ + -l list all the available options \n\ + -h key show information about this key \n\ + -g key get the current value of the key \n\ + -s key set the current value for the key \n\ + -c key clear the current value for the key \n\ + ') + +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(): + '''Build a list of pointers to available modules and import them. + ''' + modules = [] + + path = os.path.join(config.shell_path, 'controlpanel') + folder = os.listdir(path) + + for item in folder: + if os.path.isdir(os.path.join(path, item)) and \ + os.path.exists(os.path.join(path, item, 'model.py')): + module = __import__('.'.join(('controlpanel', item, 'model')), + globals(), locals(), ['model']) + modules.append(module) + + return modules + +def main(): + try: + options, args = getopt.getopt(sys.argv[1:], "h:s:g:c:l", []) + except getopt.GetoptError: + cmd_help() + sys.exit(2) + + if not options: + cmd_help() + sys.exit(2) + + modules = load_modules() + + for option, key in options: + found = 0 + if option in ("-h"): + for module in modules: + method = getattr(module, 'set_' + key, None) + if method: + found += 1 + if found == 1: + print method.__doc__ + else: + print _(_same_option_warning % (key, module)) + if found == 0: + print _(_no_option_error % key) + if option 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 option in ("-g"): + for module in modules: + method = getattr(module, 'print_' + key, None) + if method: + found += 1 + if found == 1: + try: + method() + except Exception, detail: + print _(_general_error % detail) + else: + print _(_same_option_warning % (key, module)) + if found == 0: + print _(_no_option_error % key) + if option in ("-s"): + for module in modules: + method = getattr(module, 'set_' + key, None) + if method: + note = 0 + found += 1 + if found == 1: + try: + note = method(*args) + except Exception, detail: + print _(_general_error % detail) + if note == _RESTART: + note_restart() + else: + print _(_same_option_warning % (key, module)) + if found == 0: + print _(_no_option_error % key) + if option in ("-c"): + for module in modules: + method = getattr(module, 'clear_' + key, None) + if method: + note = 0 + found += 1 + if found == 1: + try: + note = method(*args) + except Exception, detail: + print _(_general_error % detail) + if note == _RESTART: + note_restart() + else: + print _(_same_option_warning % (key, module)) + if found == 0: + print _(_no_option_error % key) diff --git a/src/jarabe/controlpanel/datetime/Makefile.am b/src/jarabe/controlpanel/datetime/Makefile.am new file mode 100644 index 0000000..f522683 --- /dev/null +++ b/src/jarabe/controlpanel/datetime/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/datetime + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/datetime/__init__.py b/src/jarabe/controlpanel/datetime/__init__.py new file mode 100644 index 0000000..fc9be45 --- /dev/null +++ b/src/jarabe/controlpanel/datetime/__init__.py @@ -0,0 +1,21 @@ +# 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 + +from gettext import gettext as _ + +CLASS = 'TimeZone' +ICON = 'module-date_and_time' +TITLE = _('Date & Time') diff --git a/src/jarabe/controlpanel/datetime/model.py b/src/jarabe/controlpanel/datetime/model.py new file mode 100644 index 0000000..4a4c560 --- /dev/null +++ b/src/jarabe/controlpanel/datetime/model.py @@ -0,0 +1,94 @@ +# 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 + +_zone_tab = '/usr/share/zoneinfo/zone.tab' + +def _initialize(): + '''Initialize the docstring of the set function''' + if set_timezone.__doc__ is None: + # when running under 'python -OO', all __doc__ fields are None, + # so += would fail -- and this function would be unnecessary anyway. + return + timezones = read_all_timezones() + for timezone in timezones: + set_timezone.__doc__ += timezone + '\n' + +def read_all_timezones(fn=_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(-12, 13): + if offset < 0: + tz = 'GMT%d' % offset + elif offset > 0: + tz = 'GMT+%d' % offset + else: + tz = 'GMT' + timezones.append(tz) + for offset in xrange(-12, 13): + if offset < 0: + tz = 'UTC%d' % offset + elif offset > 0: + tz = 'UTC+%d' % offset + else: + tz = 'UTC' + 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 + timezone : e.g. 'America/Los_Angeles' + """ + timezones = read_all_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 1 + +# inilialize the docstrings for the timezone +_initialize() + diff --git a/src/jarabe/controlpanel/datetime/view.py b/src/jarabe/controlpanel/datetime/view.py new file mode 100644 index 0000000..58719b4 --- /dev/null +++ b/src/jarabe/controlpanel/datetime/view.py @@ -0,0 +1,138 @@ +# 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 gobject +from gettext import gettext as _ + +from sugar.graphics import style +from sugar.graphics import iconentry + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +class TimeZone(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + self._zone_sid = 0 + self._cursor_change_handler = None + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + + self.connect("realize", self.__realize_cb) + + 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_all_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._timezone_column = gtk.TreeViewColumn(_('Timezone')) + self._cell = gtk.CellRendererText() + self._timezone_column.pack_start(self._cell, True) + self._timezone_column.add_attribute(self._cell, 'text', 0) + self._timezone_column.set_sort_column_id(0) + self._treeview.append_column(self._timezone_column) + + self.pack_start(self._scrolled_window) + self._scrolled_window.show() + + self._zone_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self.pack_start(self._zone_alert_box, False) + + self._zone_alert = InlineAlert() + self._zone_alert_box.pack_start(self._zone_alert) + if 'zone' in self.restart_alerts: + self._zone_alert.props.msg = self.restart_msg + self._zone_alert.show() + self._zone_alert_box.show() + + self.setup() + + def setup(self): + zone = self._model.get_timezone() + for row in self._store: + if zone == row[0]: + self._treeview.set_cursor(row.path, self._timezone_column, + False) + self._treeview.scroll_to_cell(row.path, self._timezone_column, + True, 0.5, 0.5) + break + + self.needs_restart = False + self._cursor_change_handler = self._treeview.connect( \ + "cursor-changed", self.__zone_changed_cd) + + def undo(self): + self._treeview.disconnect(self._cursor_change_handler) + self._model.undo() + self._zone_alert.hide() + + def __realize_cb(self, widget): + self._entry.grab_focus() + + def _search(self, model, column, key, iterator, data=None): + value = model.get_value(iterator, column) + if key.lower() in value.lower(): + return False + return True + + def __zone_changed_cd(self, treeview, data=None): + list_, row = treeview.get_selection().get_selected() + if not row: + return False + if self._model.get_timezone() == self._store.get_value(row, 0): + return False + + if self._zone_sid: + gobject.source_remove(self._zone_sid) + self._zone_sid = gobject.timeout_add(self._APPLY_TIMEOUT, + self.__zone_timeout_cb, row) + return True + + def __zone_timeout_cb(self, row): + self._zone_sid = 0 + self._model.set_timezone(self._store.get_value(row, 0)) + self.restart_alerts.append('zone') + self.needs_restart = True + self._zone_alert.props.msg = self.restart_msg + self._zone_alert.show() + return False diff --git a/src/jarabe/controlpanel/frame/Makefile.am b/src/jarabe/controlpanel/frame/Makefile.am new file mode 100644 index 0000000..10e2b40 --- /dev/null +++ b/src/jarabe/controlpanel/frame/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/frame + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/frame/__init__.py b/src/jarabe/controlpanel/frame/__init__.py new file mode 100644 index 0000000..a93f9c7 --- /dev/null +++ b/src/jarabe/controlpanel/frame/__init__.py @@ -0,0 +1,21 @@ +# 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 + +from gettext import gettext as _ + +CLASS = 'Frame' +ICON = 'module-frame' +TITLE = _('Frame') diff --git a/src/jarabe/controlpanel/frame/model.py b/src/jarabe/controlpanel/frame/model.py new file mode 100644 index 0000000..0e19703 --- /dev/null +++ b/src/jarabe/controlpanel/frame/model.py @@ -0,0 +1,64 @@ +# 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 + +def get_corner_delay(): + pro = profile.get_profile() + return pro.hot_corners_delay + +def print_corner_delay(): + print get_corner_delay() + +def set_corner_delay(delay): + """Set a delay for the activation of the frame using hot corners. + instantaneous: 0 (0 milliseconds) + delay: 100 (100 milliseconds) + never: 1000 (disable activation) + """ + try: + int(delay) + except ValueError: + raise ValueError(_("Value must be an integer.")) + pro = profile.get_profile() + pro.hot_corners_delay = int(delay) + pro.save() + return 1 + +def get_edge_delay(): + pro = profile.get_profile() + return pro.warm_edges_delay + +def print_edge_delay(): + print get_edge_delay() + +def set_edge_delay(delay): + """Set a delay for the activation of the frame using warm edges. + instantaneous: 0 (0 milliseconds) + delay: 100 (100 milliseconds) + never: 1000 (disable activation) + """ + try: + int(delay) + except ValueError: + raise ValueError(_("Value must be an integer.")) + pro = profile.get_profile() + pro.warm_edges_delay = int(delay) + pro.save() + return 1 diff --git a/src/jarabe/controlpanel/frame/view.py b/src/jarabe/controlpanel/frame/view.py new file mode 100644 index 0000000..7ab7bd2 --- /dev/null +++ b/src/jarabe/controlpanel/frame/view.py @@ -0,0 +1,232 @@ +# 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 gobject +from gettext import gettext as _ + +from sugar.graphics import style + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +_never = _('never') +_instantaneous = _('instantaneous') +_seconds_label = _('%s seconds') +_MAX_DELAY = 1000.0 + +class Frame(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self._corner_delay_sid = 0 + self._corner_delay_is_valid = True + self._corner_delay_change_handler = None + self._edge_delay_sid = 0 + self._edge_delay_is_valid = True + self._edge_delay_change_handler = None + self.restart_alerts = alerts + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + self._group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + + separator = gtk.HSeparator() + self.pack_start(separator, expand=False) + separator.show() + + label_activation = gtk.Label(_('Activation Delay')) + label_activation.set_alignment(0, 0) + self.pack_start(label_activation, expand=False) + label_activation.show() + + self._box_sliders = gtk.VBox() + self._box_sliders.set_border_width(style.DEFAULT_SPACING * 2) + self._box_sliders.set_spacing(style.DEFAULT_SPACING) + + self._corner_delay_slider = None + self._corner_delay_alert = None + self._setup_corner() + + self._edge_delay_slider = None + self._edge_delay_alert = None + self._setup_edge() + + self.pack_start(self._box_sliders, expand=False) + self._box_sliders.show() + + self.setup() + + def _setup_corner(self): + box_delay = gtk.HBox(spacing=style.DEFAULT_SPACING) + label_delay = gtk.Label(_('Corner')) + label_delay.set_alignment(1, 0.75) + label_delay.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + box_delay.pack_start(label_delay, expand=False) + self._group.add_widget(label_delay) + label_delay.show() + + adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, + step_incr=100, page_incr=100, page_size=0) + self._corner_delay_slider = gtk.HScale(adj) + self._corner_delay_slider.set_digits(0) + self._corner_delay_slider.connect('format-value', + self.__corner_delay_format_cb) + box_delay.pack_start(self._corner_delay_slider) + self._corner_delay_slider.show() + self._box_sliders.pack_start(box_delay, expand=False) + box_delay.show() + + self._corner_delay_alert = InlineAlert() + label_delay_error = gtk.Label() + self._group.add_widget(label_delay_error) + + delay_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + delay_alert_box.pack_start(label_delay_error, expand=False) + label_delay_error.show() + delay_alert_box.pack_start(self._corner_delay_alert, expand=False) + self._box_sliders.pack_start(delay_alert_box, expand=False) + delay_alert_box.show() + if 'corner_delay' in self.restart_alerts: + self._corner_delay_alert.props.msg = self.restart_msg + self._corner_delay_alert.show() + + def _setup_edge(self): + box_delay = gtk.HBox(spacing=style.DEFAULT_SPACING) + label_delay = gtk.Label(_('Edge')) + label_delay.set_alignment(1, 0.75) + label_delay.modify_fg(gtk.STATE_NORMAL, + style.COLOR_SELECTION_GREY.get_gdk_color()) + box_delay.pack_start(label_delay, expand=False) + self._group.add_widget(label_delay) + label_delay.show() + + adj = gtk.Adjustment(value=100, lower=0, upper=_MAX_DELAY, + step_incr=100, page_incr=100, page_size=0) + self._edge_delay_slider = gtk.HScale(adj) + self._edge_delay_slider.set_digits(0) + self._edge_delay_slider.connect('format-value', + self.__edge_delay_format_cb) + box_delay.pack_start(self._edge_delay_slider) + self._edge_delay_slider.show() + self._box_sliders.pack_start(box_delay, expand=False) + box_delay.show() + + self._edge_delay_alert = InlineAlert() + label_delay_error = gtk.Label() + self._group.add_widget(label_delay_error) + + delay_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + delay_alert_box.pack_start(label_delay_error, expand=False) + label_delay_error.show() + delay_alert_box.pack_start(self._edge_delay_alert, expand=False) + self._box_sliders.pack_start(delay_alert_box, expand=False) + delay_alert_box.show() + if 'edge_delay' in self.restart_alerts: + self._edge_delay_alert.props.msg = self.restart_msg + self._edge_delay_alert.show() + + def setup(self): + self._corner_delay_slider.set_value(self._model.get_corner_delay()) + self._edge_delay_slider.set_value(self._model.get_edge_delay()) + self._corner_delay_is_valid = True + self._edge_delay_is_valid = True + self.needs_restart = False + self._corner_delay_change_handler = self._corner_delay_slider.connect( \ + 'value-changed', self.__corner_delay_changed_cb) + self._edge_delay_change_handler = self._edge_delay_slider.connect( \ + 'value-changed', self.__edge_delay_changed_cb) + + def undo(self): + self._corner_delay_slider.disconnect(self._corner_delay_change_handler) + self._edge_delay_slider.disconnect(self._edge_delay_change_handler) + self._model.undo() + self._corner_delay_alert.hide() + self._edge_delay_alert.hide() + + def _validate(self): + if self._edge_delay_is_valid and self._corner_delay_is_valid: + self.props.is_valid = True + else: + self.props.is_valid = False + + def __corner_delay_changed_cb(self, scale, data=None): + if self._corner_delay_sid: + gobject.source_remove(self._corner_delay_sid) + self._corner_delay_sid = gobject.timeout_add( \ + self._APPLY_TIMEOUT, self.__corner_delay_timeout_cb, scale) + + def __corner_delay_timeout_cb(self, scale): + self._corner_delay_sid = 0 + if scale.get_value() == self._model.get_corner_delay(): + return + try: + self._model.set_corner_delay(scale.get_value()) + except ValueError, detail: + self._corner_delay_alert.props.msg = detail + self._corner_delay_is_valid = False + else: + self._corner_delay_alert.props.msg = self.restart_msg + self._corner_delay_is_valid = True + self.needs_restart = True + self.restart_alerts.append('corner_delay') + + self._validate() + self._corner_delay_alert.show() + return False + + def __corner_delay_format_cb(self, scale, value): + if value == _MAX_DELAY: + return _never + elif value == 0.0: + return _instantaneous + else: + return _seconds_label % (value / _MAX_DELAY) + + def __edge_delay_changed_cb(self, scale, data=None): + if self._edge_delay_sid: + gobject.source_remove(self._edge_delay_sid) + self._edge_delay_sid = gobject.timeout_add( \ + self._APPLY_TIMEOUT, self.__edge_delay_timeout_cb, scale) + + def __edge_delay_timeout_cb(self, scale): + self._edge_delay_sid = 0 + if scale.get_value() == self._model.get_edge_delay(): + return + try: + self._model.set_edge_delay(scale.get_value()) + except ValueError, detail: + self._edge_delay_alert.props.msg = detail + self._edge_delay_is_valid = False + else: + self._edge_delay_alert.props.msg = self.restart_msg + self._edge_delay_is_valid = True + self.needs_restart = True + self.restart_alerts.append('edge_delay') + + self._validate() + self._edge_delay_alert.show() + return False + + def __edge_delay_format_cb(self, scale, value): + if value == _MAX_DELAY: + return _never + elif value == 0.0: + return _instantaneous + else: + return _seconds_label % (value / _MAX_DELAY) diff --git a/src/jarabe/controlpanel/gui.py b/src/jarabe/controlpanel/gui.py new file mode 100644 index 0000000..a0e4498 --- /dev/null +++ b/src/jarabe/controlpanel/gui.py @@ -0,0 +1,414 @@ +# 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 os +import gobject +import logging +from gettext import gettext as _ + +from sugar.graphics.icon import Icon +from sugar.graphics import style +from sugar.graphics.alert import Alert +import config +from session import get_session_manager + +from jarabe.controlpanel.toolbar import MainToolbar +from jarabe.controlpanel.toolbar import SectionToolbar + +_logger = logging.getLogger('ControlPanel') +_MAX_COLUMNS = 5 + +class ControlPanel(gtk.Window): + __gtype_name__ = 'SugarControlPanel' + + def __init__(self): + gtk.Window.__init__(self) + + 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._scrolledwindow = None + self._separator = None + self._section_view = None + self._section_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._get_options() + self._current_option = None + self._setup_main() + self._setup_section() + self._show_main_view() + + def __realize_cb(self, widget): + self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) + self.window.set_accept_focus(True) + + 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_col_spacings(style.GRID_CELL_SIZE) + self._table.set_border_width(style.GRID_CELL_SIZE) + + self._scrolledwindow = gtk.ScrolledWindow() + self._scrolledwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self._scrolledwindow.add_with_viewport(self._table) + child = self._scrolledwindow.get_child() + child.modify_bg(gtk.STATE_NORMAL, style.COLOR_BLACK.get_gdk_color()) + + 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 = 2 + options = self._options.keys() + options.sort() + + for option in options: + sectionicon = _SectionIcon(icon_name=self._options[option]['icon'], + title=self._options[option]['title'], + xo_color=self._options[option]['color'], + pixel_size=style.GRID_CELL_SIZE) + sectionicon.connect('button_press_event', + self.__select_option_cb, option) + sectionicon.show() + + if option == 'aboutme': + self._table.attach(sectionicon, 0, 1, 0, 1) + elif option == 'aboutxo': + self._table.attach(sectionicon, 1, 2, 0, 1) + else: + self._table.attach(sectionicon, + column, column + 1, + row, row + 1) + column += 1 + if column == _MAX_COLUMNS: + column = 0 + row += 1 + + self._options[option]['button'] = sectionicon + + def _show_main_view(self): + self._set_toolbar(self._main_toolbar) + self._main_toolbar.show() + self._set_canvas(self._scrolledwindow) + self._main_view.modify_bg(gtk.STATE_NORMAL, + style.COLOR_BLACK.get_gdk_color()) + self._table.show() + self._scrolledwindow.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.lower() in key.lower(): + self._options[option]['button'].set_sensitive(True) + found = True + break + if not found: + self._options[option]['button'].set_sensitive(False) + + def _setup_section(self): + self._section_toolbar = SectionToolbar() + self._section_toolbar.connect('cancel-clicked', + self.__cancel_clicked_cb) + self._section_toolbar.connect('accept-clicked', + self.__accept_clicked_cb) + + def show_section_view(self, option): + self._set_toolbar(self._section_toolbar) + + icon = self._section_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._section_toolbar.get_title() + title.set_text(self._options[option]['title']) + self._section_toolbar.show() + + self._current_option = option + + mod = __import__('.'.join(('controlpanel', option, 'view')), + globals(), locals(), ['view']) + view_class = getattr(mod, self._options[option]['view'], None) + + mod = __import__('.'.join(('controlpanel', option, 'model')), + globals(), locals(), ['model']) + model = ModelWrapper(mod) + + self._section_view = view_class(model, + self._options[option]['alerts']) + + self._set_canvas(self._section_view) + self._section_view.show() + self._section_view.connect('notify::is-valid', + self.__valid_section_cb) + self._section_view.connect('request-close', + self.__close_request_cb) + self._main_view.modify_bg(gtk.STATE_NORMAL, + style.COLOR_WHITE.get_gdk_color()) + + def set_section_view_auto_close(self): + '''Automatically close the control panel if there is "nothing to do" + ''' + self._section_view.auto_close = True + + def _get_options(self): + '''Get the available option information from the extensions + ''' + options = {} + + path = os.path.join(config.shell_path, 'controlpanel') + folder = os.listdir(path) + + for item in folder: + if os.path.isdir(os.path.join(path, item)) and \ + os.path.exists(os.path.join(path, item, '__init__.py')): + mod = __import__('.'.join(('controlpanel', item)), + globals(), locals(), [item]) + view_class = getattr(mod, 'CLASS', None) + if view_class is not None: + options[item] = {} + options[item]['alerts'] = [] + options[item]['view'] = view_class + options[item]['icon'] = getattr(mod, 'ICON', item) + options[item]['title'] = getattr(mod, 'TITLE', item) + options[item]['color'] = getattr(mod, 'COLOR', None) + keywords = getattr(mod, 'KEYWORDS', []) + keywords.append(options[item]['title'].lower()) + if item not in keywords: + keywords.append(item) + else: + _logger.error('There is no CLASS constant specified in ' \ + 'the view file \'%s\'.' % item) + return options + + def __cancel_clicked_cb(self, widget): + self._section_view.undo() + self._options[self._current_option]['alerts'] = [] + self._section_toolbar.accept_button.set_sensitive(True) + self._show_main_view() + + def __accept_clicked_cb(self, widget): + if self._section_view.needs_restart: + self._section_toolbar.accept_button.set_sensitive(False) + self._section_toolbar.cancel_button.set_sensitive(False) + alert = Alert() + alert.props.title = _('Warning') + alert.props.msg = _('Changes require restart') + + icon = Icon(icon_name='dialog-cancel') + alert.add_button(gtk.RESPONSE_CANCEL, _('Cancel changes'), icon) + icon.show() + + if self._current_option != 'aboutme': + icon = Icon(icon_name='dialog-ok') + alert.add_button(gtk.RESPONSE_ACCEPT, _('Later'), icon) + icon.show() + + icon = Icon(icon_name='system-restart') + alert.add_button(gtk.RESPONSE_APPLY, _('Restart now'), icon) + icon.show() + + 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) + self._section_toolbar.accept_button.set_sensitive(True) + self._section_toolbar.cancel_button.set_sensitive(True) + if response_id is gtk.RESPONSE_CANCEL: + self._section_view.undo() + self._section_view.setup() + self._options[self._current_option]['alerts'] = [] + elif response_id is gtk.RESPONSE_ACCEPT: + self._options[self._current_option]['alerts'] = \ + self._section_view.restart_alerts + self._show_main_view() + elif response_id is gtk.RESPONSE_APPLY: + session_manager = get_session_manager() + session_manager.logout() + + def __select_option_cb(self, button, event, option): + self.show_section_view(option) + + def __search_changed_cb(self, maintoolbar, query): + self._update(query) + + def __stop_clicked_cb(self, widget): + self.destroy() + + def __close_request_cb(self, widget, event=None): + self.destroy() + + def __valid_section_cb(self, section_view, pspec): + section_is_valid = section_view.props.is_valid + self._section_toolbar.accept_button.set_sensitive(section_is_valid) + +class ModelWrapper(object): + def __init__(self, module): + self._module = module + self._options = {} + self._setup() + + def _setup(self): + methods = dir(self._module) + for method in methods: + if method.startswith('get_') and method[4:] != 'color': + try: + self._options[method[4:]] = getattr(self._module, method)() + except Exception: + self._options[method[4:]] = None + + def __getattr__(self, name): + return getattr(self._module, name) + + def undo(self): + for key in self._options.keys(): + method = getattr(self._module, 'set_' + key, None) + if method and self._options[key] is not None: + try: + method(self._options[key]) + except Exception, detail: + _logger.debug('Error undo option: %s' % detail) + +class _SectionIcon(gtk.EventBox): + __gtype_name__ = "SugarSectionIcon" + + __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/jarabe/controlpanel/inlinealert.py b/src/jarabe/controlpanel/inlinealert.py new file mode 100644 index 0000000..619a379 --- /dev/null +++ b/src/jarabe/controlpanel/inlinealert.py @@ -0,0 +1,83 @@ +# 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 gobject +import pango + +from sugar.graphics import style +from sugar.graphics.icon import Icon + +class InlineAlert(gtk.HBox): + """UI interface for Inline alerts + + Inline alerts are different from the other alerts beause they are + no dialogs, they only inform about a current event. + + Properties: + 'msg': 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), + 'icon' : (object, None, None, + gobject.PARAM_WRITABLE) + } + + def __init__(self, **kwargs): + + self._msg = None + self._msg_color = None + self._icon = Icon(icon_name='emblem-warning', + fill_color=style.COLOR_SELECTION_GREY.get_svg(), + stroke_color=style.COLOR_WHITE.get_svg()) + + 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()) + + gobject.GObject.__init__(self, **kwargs) + + self.set_spacing(style.DEFAULT_SPACING) + self.modify_bg(gtk.STATE_NORMAL, + style.COLOR_WHITE.get_gdk_color()) + + self.pack_start(self._icon, False) + self.pack_start(self._msg_label, False) + self._msg_label.show() + self._icon.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 + + def do_get_property(self, pspec): + if pspec.name == 'msg': + return self._msg + diff --git a/src/jarabe/controlpanel/language/Makefile.am b/src/jarabe/controlpanel/language/Makefile.am new file mode 100644 index 0000000..ed8992c --- /dev/null +++ b/src/jarabe/controlpanel/language/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/language + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/language/__init__.py b/src/jarabe/controlpanel/language/__init__.py new file mode 100644 index 0000000..a8f9f08 --- /dev/null +++ b/src/jarabe/controlpanel/language/__init__.py @@ -0,0 +1,22 @@ +# 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 + +from gettext import gettext as _ + +CLASS = 'Language' +ICON = 'module-language' +TITLE = _('Language') + diff --git a/src/jarabe/controlpanel/language/model.py b/src/jarabe/controlpanel/language/model.py new file mode 100644 index 0000000..404d9dd --- /dev/null +++ b/src/jarabe/controlpanel/language/model.py @@ -0,0 +1,135 @@ +# 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 + +_default_lang = 'en_US.utf8' +_standard_msg = _("Could not access ~/.i18n. Create standard settings.") + +def read_all_languages(): + fdp = subprocess.Popen(['locale', '-av'], stdout=subprocess.PIPE) + lines = fdp.stdout.read().split('\n') + locales = [] + + for line in lines: + if line.find('locale:') != -1: + locale = line.lstrip('locale:') + locale = locale.split('archive:')[0].strip() + elif line.find('language |') != -1: + lang = line.lstrip('language |') + elif line.find('territory |') != -1: + territory = line.lstrip('territory |') + if locale.endswith('utf8') and len(lang): + locales.append((lang, territory, locale)) + + #FIXME: This is a temporary workaround for locales that are essential to + # OLPC, but are not in Glibc yet. + locales.append(('Kreyol', 'Haiti', 'ht_HT.utf8')) + locales.append(('Dari', 'Afghanistan', 'fa_AF.utf8')) + locales.append(('Pashto', 'Afghanistan', 'ps_AF.utf8')) + + locales.sort() + return locales + +def _initialize(): + if set_language.__doc__ is None: + # when running under 'python -OO', all __doc__ fields are None, + # so += would fail -- and this function would be unnecessary anyway. + return + languages = read_all_languages() + 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 _standard_msg + fd = open(path, 'w') + fd.write('LANG="%s"\n' % _default_lang) + 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="%s"\n' % lang + 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 _standard_msg + fd = open(path, 'w') + fd.write('LANG="%s"\n' % _default_lang) + fd.close() + return _default_lang + + 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 = read_all_languages() + for lang in languages: + if lang[2].split('.')[0] == code.split('.')[0]: + 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 1 + else: + languages = read_all_languages() + for lang, territory, locale in languages: + code = lang.replace(' ', '_') + '/' \ + + territory.replace(' ', '_') + if code == language: + _write_i18n(locale) + return 1 + print (_("Sorry I do not speak \'%s\'.") % language) + +# inilialize the docstrings for the language +_initialize() + diff --git a/src/jarabe/controlpanel/language/view.py b/src/jarabe/controlpanel/language/view.py new file mode 100644 index 0000000..71a185f --- /dev/null +++ b/src/jarabe/controlpanel/language/view.py @@ -0,0 +1,142 @@ +# 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 gobject +from gettext import gettext as _ + +from sugar.graphics import style +from sugar.graphics import iconentry + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +class Language(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + self._lang_sid = 0 + self._cursor_change_handler = None + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + + self.connect("realize", self.__realize_cb) + + 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.read_all_languages() + 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._language_column = gtk.TreeViewColumn(_('Language')) + self._cell = gtk.CellRendererText() + self._language_column.pack_start(self._cell, True) + self._language_column.add_attribute(self._cell, 'text', 1) + self._language_column.set_sort_column_id(1) + self._treeview.append_column(self._language_column) + + self.pack_start(self._scrolled_window) + self._scrolled_window.show() + + self._lang_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self.pack_start(self._lang_alert_box, False) + + self._lang_alert = InlineAlert() + self._lang_alert_box.pack_start(self._lang_alert) + if 'lang' in self.restart_alerts: + self._lang_alert.props.msg = self.restart_msg + self._lang_alert.show() + self._lang_alert_box.show() + + self.setup() + + def setup(self): + lang_code = self._model.get_language() + for row in self._store: + lang = lang_code.split('.')[0] + lang_column = row[0].split('.')[0] + if lang in lang_column: + self._treeview.set_cursor(row.path, self._language_column, + False) + self._treeview.scroll_to_cell(row.path, self._language_column, + True, 0.5, 0.5) + break + + self.needs_restart = False + self._cursor_change_handler = self._treeview.connect( \ + "cursor-changed", self.__lang_changed_cd) + + def undo(self): + self._treeview.disconnect(self._cursor_change_handler) + self._model.undo() + self._lang_alert.hide() + + def __realize_cb(self, widget): + self._entry.grab_focus() + + def _search(self, model, column, key, iterator, data=None): + value = model.get_value(iterator, column) + if key.lower() in value.lower(): + return False + return True + + def __lang_changed_cd(self, treeview, data=None): + row = treeview.get_selection().get_selected() + if not row[1]: + return False + if self._model.get_language() == self._store.get_value(row[1], 0): + return False + + if self._lang_sid: + gobject.source_remove(self._lang_sid) + self._lang_sid = gobject.timeout_add(self._APPLY_TIMEOUT, + 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_alerts.append('lang') + self.needs_restart = True + self._lang_alert.props.msg = self.restart_msg + self._lang_alert.show() + return False diff --git a/src/jarabe/controlpanel/network/Makefile.am b/src/jarabe/controlpanel/network/Makefile.am new file mode 100644 index 0000000..e480aa3 --- /dev/null +++ b/src/jarabe/controlpanel/network/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/network + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/network/__init__.py b/src/jarabe/controlpanel/network/__init__.py new file mode 100644 index 0000000..8fea274 --- /dev/null +++ b/src/jarabe/controlpanel/network/__init__.py @@ -0,0 +1,25 @@ +# Copyright (C) 2006-2007, Red Hat, Inc. +# +# 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 _ + +CLASS = 'Network' +ICON = 'module-network' +TITLE = _('Network') +KEYWORDS = ['network', 'jabber', 'radio', 'server'] + + + diff --git a/src/jarabe/controlpanel/network/model.py b/src/jarabe/controlpanel/network/model.py new file mode 100644 index 0000000..b6f71a0 --- /dev/null +++ b/src/jarabe/controlpanel/network/model.py @@ -0,0 +1,101 @@ +# 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 +from jarabe.hardware import hardwaremanager + +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' + """ + pro = profile.get_profile() + pro.jabber_server = server + pro.jabber_registered = False + pro.save() + return 1 + +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 in (0, 1): + return state + else: + raise ReadError(_('State is unknown.')) + +def print_radio(): + print ('off', 'on')[get_radio()] + +def set_radio(state): + """Turn Radio 'on' or 'off' + state : 'on/off' + """ + if state == 'on' or state == 1: + 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' or state == 0: + 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.")) + + return 0 + +def clear_registration(): + """Clear the registration with the schoolserver + """ + pro = profile.get_profile() + pro.backup1 = None + pro.save() + return 1 + +def clear_networks(): + """Clear saved passwords and network configurations. + """ + network_manager = hardwaremanager.get_network_manager() + if not network_manager: + return + network_manager.nminfo.delete_all_networks() + return 1 diff --git a/src/jarabe/controlpanel/network/view.py b/src/jarabe/controlpanel/network/view.py new file mode 100644 index 0000000..4f4ada7 --- /dev/null +++ b/src/jarabe/controlpanel/network/view.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 gobject +from gettext import gettext as _ + +from sugar.graphics import style + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +CLASS = 'Network' +ICON = 'module-network' +TITLE = _('Network') + +class Network(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + self._jabber_sid = 0 + self._jabber_valid = True + self._radio_valid = True + self._jabber_change_handler = None + self._radio_change_handler = None + self._network_configuration_reset_handler = None + + 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) + + radio_info = gtk.Label(_("Turn of the wireless radio to save " + "battery life")) + radio_info.set_alignment(0, 0) + radio_info.set_line_wrap(True) + radio_info.show() + box_wireless.pack_start(radio_info, expand=False) + + box_radio = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._button = gtk.CheckButton() + self._button.set_alignment(0, 0) + box_radio.pack_start(self._button, expand=False) + self._button.show() + + label_radio = gtk.Label(_('Radio')) + label_radio.set_alignment(0, 0.5) + box_radio.pack_start(label_radio, expand=False) + label_radio.show() + + box_wireless.pack_start(box_radio, expand=False) + box_radio.show() + + self._radio_alert = InlineAlert() + self._radio_alert_box.pack_start(self._radio_alert, expand=False) + box_radio.pack_end(self._radio_alert_box, expand=False) + self._radio_alert_box.show() + if 'radio' in self.restart_alerts: + self._radio_alert.props.msg = self.restart_msg + self._radio_alert.show() + + history_info = gtk.Label(_("Discard network history if you " + "have trouble connecting to the network")) + history_info.set_alignment(0, 0) + history_info.set_line_wrap(True) + history_info.show() + box_wireless.pack_start(history_info, expand=False) + + box_clear_history = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._clear_history_button = gtk.Button() + self._clear_history_button.set_label(_('Discard network history')) + box_clear_history.pack_start(self._clear_history_button, expand=False) + self._clear_history_button.show() + box_wireless.pack_start(box_clear_history, expand=False) + box_clear_history.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) + box_server.pack_start(self._entry, expand=False) + self._entry.show() + box_mesh.pack_start(box_server, expand=False) + box_server.show() + + self._jabber_alert = InlineAlert() + 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() + + self.setup() + + def setup(self): + self._entry.set_text(self._model.get_jabber()) + try: + radio_state = self._model.get_radio() + except Exception, detail: + self._radio_alert.props.msg = detail + self._radio_alert.show() + else: + self._button.set_active(radio_state) + + self._jabber_valid = True + self._radio_valid = True + self.needs_restart = False + self._radio_change_handler = self._button.connect( \ + 'toggled', self.__radio_toggled_cb) + self._jabber_change_handler = self._entry.connect( \ + 'changed', self.__jabber_changed_cb) + self._network_configuration_reset_handler = \ + self._clear_history_button.connect( \ + 'clicked', self.__network_configuration_reset_cb) + + def undo(self): + self._button.disconnect(self._radio_change_handler) + self._entry.disconnect(self._jabber_change_handler) + self._model.undo() + self._jabber_alert.hide() + self._radio_alert.hide() + + def _validate(self): + if self._jabber_valid and self._radio_valid: + self.props.is_valid = True + else: + self.props.is_valid = False + + def __radio_toggled_cb(self, widget, data=None): + radio_state = 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_valid = True + + self._validate() + 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(self._APPLY_TIMEOUT, + self.__jabber_timeout_cb, widget) + + def __jabber_timeout_cb(self, widget): + self._jabber_sid = 0 + if widget.get_text() == self._model.get_jabber: + return + 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 + self.needs_restart = True + self.restart_alerts.append('jabber') + + self._validate() + self._jabber_alert.show() + return False + + def __network_configuration_reset_cb(self, widget): + self._model.clear_networks() diff --git a/src/jarabe/controlpanel/power/Makefile.am b/src/jarabe/controlpanel/power/Makefile.am new file mode 100644 index 0000000..e989e97 --- /dev/null +++ b/src/jarabe/controlpanel/power/Makefile.am @@ -0,0 +1,6 @@ +sugardir = $(pythondir)/jarabe/controlpanel/power + +sugar_PYTHON = \ + __init__.py \ + model.py \ + view.py diff --git a/src/jarabe/controlpanel/power/__init__.py b/src/jarabe/controlpanel/power/__init__.py new file mode 100644 index 0000000..8b2e85f --- /dev/null +++ b/src/jarabe/controlpanel/power/__init__.py @@ -0,0 +1,23 @@ +# 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 + +from gettext import gettext as _ + +CLASS = 'Power' +ICON = 'module-power' +TITLE = _('Power') +KEYWORDS = ['automatic', 'extreme', 'power', 'suspend', 'battery'] + diff --git a/src/jarabe/controlpanel/power/model.py b/src/jarabe/controlpanel/power/model.py new file mode 100644 index 0000000..47af483 --- /dev/null +++ b/src/jarabe/controlpanel/power/model.py @@ -0,0 +1,89 @@ +# 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 +import dbus + +OHM_SERVICE_NAME = 'org.freedesktop.ohm' +OHM_SERVICE_PATH = '/org/freedesktop/ohm/Keystore' +OHM_SERVICE_IFACE = 'org.freedesktop.ohm.Keystore' + +class ReadError(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +def get_automatic_pm(): + pro = profile.get_profile() + ret = pro.automatic_pm + return ret + +def print_automatic_pm(): + print ('off', 'on')[get_automatic_pm()] + +def set_automatic_pm(enabled): + """Automatic suspends on/off.""" + + bus = dbus.SystemBus() + proxy = bus.get_object(OHM_SERVICE_NAME, OHM_SERVICE_PATH) + keystore = dbus.Interface(proxy, OHM_SERVICE_IFACE) + + if enabled == 'on' or enabled == 1: + keystore.SetKey("suspend.automatic_pm", 1) + enabled = True + elif enabled == 'off' or enabled == 0: + keystore.SetKey("suspend.automatic_pm", 0) + enabled = False + else: + raise ValueError(_("Error in automatic pm argument, use on/off.")) + + pro = profile.get_profile() + pro.automatic_pm = enabled + pro.save() + return 0 + +def get_extreme_pm(): + pro = profile.get_profile() + ret = pro.extreme_pm + return ret + +def print_extreme_pm(): + print ('off', 'on')[get_extreme_pm()] + +def set_extreme_pm(enabled): + """Extreme power management on/off.""" + + bus = dbus.SystemBus() + proxy = bus.get_object(OHM_SERVICE_NAME, OHM_SERVICE_PATH) + keystore = dbus.Interface(proxy, OHM_SERVICE_IFACE) + + if enabled == 'on' or enabled == 1: + keystore.SetKey("suspend.extreme_pm", 1) + enabled = True + elif enabled == 'off' or enabled == 0: + keystore.SetKey("suspend.extreme_pm", 0) + enabled = False + else: + raise ValueError(_("Error in extreme pm argument, use on/off.")) + + pro = profile.get_profile() + pro.extreme_pm = enabled + pro.save() + return 0 diff --git a/src/jarabe/controlpanel/power/view.py b/src/jarabe/controlpanel/power/view.py new file mode 100644 index 0000000..8f1ed56 --- /dev/null +++ b/src/jarabe/controlpanel/power/view.py @@ -0,0 +1,177 @@ +# 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 +from gettext import gettext as _ + +from sugar.graphics import style + +from jarabe.controlpanel.sectionview import SectionView +from jarabe.controlpanel.inlinealert import InlineAlert + +class Power(SectionView): + def __init__(self, model, alerts): + SectionView.__init__(self) + + self._model = model + self.restart_alerts = alerts + self._automatic_pm_valid = True + self._extreme_pm_valid = True + self._extreme_pm_change_handler = None + self._automatic_pm_change_handler = None + + self.set_border_width(style.DEFAULT_SPACING * 2) + self.set_spacing(style.DEFAULT_SPACING) + group = gtk.SizeGroup(gtk.SIZE_GROUP_HORIZONTAL) + + self._automatic_pm_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + self._extreme_pm_alert_box = gtk.HBox(spacing=style.DEFAULT_SPACING) + + separator_pm = gtk.HSeparator() + self.pack_start(separator_pm, expand=False) + separator_pm.show() + + label_pm = gtk.Label(_('Power management')) + label_pm.set_alignment(0, 0) + self.pack_start(label_pm, expand=False) + label_pm.show() + box_pm = gtk.VBox() + box_pm.set_border_width(style.DEFAULT_SPACING * 2) + box_pm.set_spacing(style.DEFAULT_SPACING) + + box_automatic_pm = gtk.HBox(spacing=style.DEFAULT_SPACING) + label_automatic_pm = gtk.Label( + _('Automatic power management (increases battery life)')) + label_automatic_pm.set_alignment(0, 0.5) + self._automatic_button = gtk.CheckButton() + self._automatic_button.set_alignment(0, 0) + box_automatic_pm.pack_start(self._automatic_button, expand=False) + box_automatic_pm.pack_start(label_automatic_pm, expand=False) + self._automatic_button.show() + label_automatic_pm.show() + group.add_widget(label_automatic_pm) + box_pm.pack_start(box_automatic_pm, expand=False) + box_automatic_pm.show() + + self._automatic_pm_alert = InlineAlert() + label_automatic_pm_error = gtk.Label() + group.add_widget(label_automatic_pm_error) + self._automatic_pm_alert_box.pack_start(label_automatic_pm_error, + expand=False) + label_automatic_pm_error.show() + self._automatic_pm_alert_box.pack_start(self._automatic_pm_alert, + expand=False) + box_pm.pack_end(self._automatic_pm_alert_box, expand=False) + self._automatic_pm_alert_box.show() + if 'automatic_pm' in self.restart_alerts: + self._automatic_pm_alert.props.msg = self.restart_msg + self._automatic_pm_alert.show() + + box_extreme_pm = gtk.HBox(spacing=style.DEFAULT_SPACING) + label_extreme_pm = gtk.Label( + _('Extreme power management (disables' \ + 'wireless radio, increases battery life)')) + label_extreme_pm.set_alignment(0, 0.5) + self._extreme_button = gtk.CheckButton() + self._extreme_button.set_alignment(0, 0) + box_extreme_pm.pack_start(self._extreme_button, expand=False) + self._extreme_button.show() + box_extreme_pm.pack_start(label_extreme_pm, expand=False) + group.add_widget(label_extreme_pm) + label_extreme_pm.show() + box_pm.pack_start(box_extreme_pm, expand=False) + box_extreme_pm.show() + + self._extreme_pm_alert = InlineAlert() + label_extreme_pm_error = gtk.Label() + group.add_widget(label_extreme_pm_error) + self._extreme_pm_alert_box.pack_start(label_extreme_pm_error, + expand=False) + label_extreme_pm_error.show() + self._extreme_pm_alert_box.pack_start(self._extreme_pm_alert, + expand=False) + box_pm.pack_end(self._extreme_pm_alert_box, expand=False) + self._extreme_pm_alert_box.show() + if 'extreme_pm' in self.restart_alerts: + self._extreme_pm_alert.props.msg = self.restart_msg + self._extreme_pm_alert.show() + + self.pack_start(box_pm, expand=False) + box_pm.show() + + self.setup() + + def setup(self): + try: + automatic_state = self._model.get_automatic_pm() + extreme_state = self._model.get_extreme_pm() + + except Exception, detail: + self._automatic_pm_alert.props.msg = detail + self._automatic_pm_alert.show() + + self._extreme_pm_alert.props.msg = detail + self._extreme_pm_alert.show() + else: + self._automatic_button.set_active(automatic_state) + self._extreme_button.set_active(extreme_state) + + self._extreme_pm_valid = True + self._automatic_pm_valid = True + self.needs_restart = False + self._automatic_pm_change_handler = self._automatic_button.connect( \ + 'toggled', self.__automatic_pm_toggled_cb) + self._extreme_pm_change_handler = self._extreme_button.connect( \ + 'toggled', self.__extreme_pm_toggled_cb) + + def undo(self): + self._automatic_button.disconnect(self._automatic_pm_change_handler) + self._extreme_button.disconnect(self._extreme_pm_change_handler) + self._model.undo() + self._extreme_pm_alert.hide() + self._automatic_pm_alert.hide() + + def _validate(self): + if self._extreme_pm_valid and self._automatic_pm_valid: + self.props.is_valid = True + else: + self.props.is_valid = False + + def __automatic_pm_toggled_cb(self, widget, data=None): + state = widget.get_active() + try: + self._model.set_automatic_pm(state) + except Exception, detail: + print detail + self._automatic_pm_alert.props.msg = detail + else: + self._automatic_pm_valid = True + + self._validate() + return False + + def __extreme_pm_toggled_cb(self, widget, data=None): + state = widget.get_active() + try: + self._model.set_extreme_pm(state) + except Exception, detail: + print detail + self._extreme_pm_alert.props.msg = detail + else: + self._extreme_pm_valid = True + + self._validate() + return False diff --git a/src/jarabe/controlpanel/sectionview.py b/src/jarabe/controlpanel/sectionview.py new file mode 100644 index 0000000..7b76aa1 --- /dev/null +++ b/src/jarabe/controlpanel/sectionview.py @@ -0,0 +1,55 @@ +# 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 gobject +import gtk +from gettext import gettext as _ + +class SectionView(gtk.VBox): + __gtype_name__ = 'SugarSectionView' + + __gsignals__ = { + 'request-close': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, ([])) + } + + __gproperties__ = { + 'is_valid' : (bool, None, None, True, + gobject.PARAM_READWRITE) + } + + _APPLY_TIMEOUT = 1000 + + def __init__(self): + gtk.VBox.__init__(self) + self._is_valid = True + self.auto_close = False + self.needs_restart = False + self.restart_alerts = [] + self.restart_msg = _('Changes require restart') + + def do_set_property(self, pspec, value): + if pspec.name == 'is-valid': + if self._is_valid != value: + self._is_valid = value + + def do_get_property(self, pspec): + if pspec.name == 'is-valid': + return self._is_valid + + def undo(self): + '''Undo here the changes that have been made in this section.''' + pass diff --git a/src/jarabe/controlpanel/toolbar.py b/src/jarabe/controlpanel/toolbar.py new file mode 100644 index 0000000..6bb8328 --- /dev/null +++ b/src/jarabe/controlpanel/toolbar.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 toolbar of the control panel + """ + __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 SectionToolbar(gtk.Toolbar): + """ Toolbar of the sections of the control panel + """ + __gtype_name__ = 'SectionToolbar' + + __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) + + self.cancel_button = ToolButton('dialog-cancel') + self.cancel_button.set_tooltip(_('Cancel')) + self.cancel_button.connect('clicked', self.__cancel_button_clicked_cb) + self.insert(self.cancel_button, -1) + self.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') + -- cgit v0.9.1