Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell/src/jarabe/controlpanel/gui.py
diff options
context:
space:
mode:
Diffstat (limited to 'shell/src/jarabe/controlpanel/gui.py')
-rw-r--r--shell/src/jarabe/controlpanel/gui.py431
1 files changed, 431 insertions, 0 deletions
diff --git a/shell/src/jarabe/controlpanel/gui.py b/shell/src/jarabe/controlpanel/gui.py
new file mode 100644
index 0000000..51d9820
--- /dev/null
+++ b/shell/src/jarabe/controlpanel/gui.py
@@ -0,0 +1,431 @@
+# 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
+from gettext import gettext as _
+import sys
+import traceback
+
+import gobject
+import gtk
+
+from sugar.graphics.icon import Icon
+from sugar.graphics import style
+from sugar.graphics.alert import Alert
+
+from jarabe.model.session import get_session_manager
+from jarabe.controlpanel.toolbar import MainToolbar
+from jarabe.controlpanel.toolbar import SectionToolbar
+from jarabe import config
+
+_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):
+ if not os.path.exists('/ofw'):
+ del self._options['power']
+
+ try:
+ import xklavier
+ except ImportError:
+ del self._options['keyboard']
+
+ 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 == 'aboutcomputer':
+ 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(('cpsection', option, 'view')),
+ globals(), locals(), ['view'])
+ view_class = getattr(mod, self._options[option]['view'], None)
+
+ mod = __import__('.'.join(('cpsection', 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.ext_path, 'cpsection')
+ 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')):
+ try:
+ mod = __import__('.'.join(('cpsection', 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)
+ options[item]['keywords'] = keywords
+ else:
+ _logger.error('There is no CLASS constant specifieds ' \
+ 'in the view file \'%s\'.' % item)
+ except Exception:
+ logging.error('Exception while loading extension:\n' + \
+ ''.join(traceback.format_exception(*sys.exc_info())))
+
+ 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