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