From 376e117543ee6d793474ec8735ba592ce188881d Mon Sep 17 00:00:00 2001 From: Daniel Francis Date: Thu, 29 Nov 2012 18:37:12 +0000 Subject: Multiple home views Signed-off-by: Daniel Francis --- diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py index 6594ee9..da8bb65 100644 --- a/src/jarabe/desktop/activitieslist.py +++ b/src/jarabe/desktop/activitieslist.py @@ -1,5 +1,6 @@ # Copyright (C) 2008 One Laptop Per Child # Copyright (C) 2009 Tomeu Vizoso +# Copyright (C) 2012 Daniel Francis # # 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 @@ -66,9 +67,14 @@ class ActivitiesTreeView(Gtk.TreeView): cell_favorite = CellRendererFavorite(self) cell_favorite.connect('clicked', self.__favorite_clicked_cb) + cell_school = CellRendererSchool(self) + cell_school.connect('clicked', self.__school_clicked_cb) + column = Gtk.TreeViewColumn() column.pack_start(cell_favorite, True) + column.pack_start(cell_school, True) column.set_cell_data_func(cell_favorite, self.__favorite_set_data_cb) + column.set_cell_data_func(cell_school, self.__school_set_data_cb) self.append_column(column) cell_icon = CellRendererActivityIcon(self) @@ -135,6 +141,15 @@ class ActivitiesTreeView(Gtk.TreeView): else: cell.props.xo_color = None + def __school_set_data_cb(self, column, cell, model, tree_iter, data): + school = model[tree_iter][ListModel.COLUMN_SCHOOL] + if school: + client = GConf.Client.get_default() + color = XoColor(client.get_string('/desktop/sugar/user/color')) + cell.props.xo_color = color + else: + cell.props.xo_color = None + def __favorite_clicked_cb(self, cell, path): row = self.get_model()[path] registry = bundleregistry.get_registry() @@ -142,6 +157,13 @@ class ActivitiesTreeView(Gtk.TreeView): row[ListModel.COLUMN_VERSION], not row[ListModel.COLUMN_FAVORITE]) + def __school_clicked_cb(self, cell, path): + row = self.get_model()[path] + registry = bundleregistry.get_registry() + registry.set_bundle_for_school(row[ListModel.COLUMN_BUNDLE_ID], + row[ListModel.COLUMN_VERSION], + not row[ListModel.COLUMN_SCHOOL]) + def __icon_clicked_cb(self, cell, path): row = self.get_model()[path] @@ -171,15 +193,16 @@ class ListModel(Gtk.TreeModelSort): COLUMN_BUNDLE_ID = 0 COLUMN_FAVORITE = 1 - COLUMN_ICON = 2 - COLUMN_TITLE = 3 - COLUMN_VERSION = 4 - COLUMN_VERSION_TEXT = 5 - COLUMN_DATE = 6 - COLUMN_DATE_TEXT = 7 + COLUMN_SCHOOL = 2 + COLUMN_ICON = 3 + COLUMN_TITLE = 4 + COLUMN_VERSION = 5 + COLUMN_VERSION_TEXT = 6 + COLUMN_DATE = 7 + COLUMN_DATE_TEXT = 8 def __init__(self): - self._model = Gtk.ListStore(str, bool, str, str, str, str, int, str) + self._model = Gtk.ListStore(str, bool, bool, str, str, str, str, int, str) self._model_filter = self._model.filter_new() Gtk.TreeModelSort.__init__(self, model=self._model_filter) self.set_sort_column_id(ListModel.COLUMN_TITLE, Gtk.SortType.ASCENDING) @@ -201,10 +224,12 @@ class ListModel(Gtk.TreeModelSort): bundle_id = activity_info.get_bundle_id() version = activity_info.get_activity_version() favorite = activity_registry.is_bundle_favorite(bundle_id, version) + school = activity_registry.is_bundle_for_school(bundle_id, version) for row in self._model: if row[ListModel.COLUMN_BUNDLE_ID] == bundle_id and \ row[ListModel.COLUMN_VERSION] == version: row[ListModel.COLUMN_FAVORITE] = favorite + row[ListModel.COLUMN_SCHOOL] = school return def __activity_removed_cb(self, activity_registry, activity_info): @@ -226,7 +251,8 @@ class ListModel(Gtk.TreeModelSort): registry = bundleregistry.get_registry() favorite = registry.is_bundle_favorite(activity_info.get_bundle_id(), version) - + school = registry.is_bundle_for_school(activity_info.get_bundle_id(), + version) tag_list = activity_info.get_tags() if tag_list is None or not tag_list: title = '%s' % activity_info.get_name() @@ -238,6 +264,7 @@ class ListModel(Gtk.TreeModelSort): self._model.append([activity_info.get_bundle_id(), favorite, + school, activity_info.get_icon(), title, version, @@ -269,6 +296,23 @@ class CellRendererFavorite(CellRendererIcon): self.props.prelit_fill_color = prelit_color.get_fill_color() +class CellRendererSchool(CellRendererIcon): + __gtype_name__ = 'SugarCellRendererSchool' + + def __init__(self, tree_view): + CellRendererIcon.__init__(self, tree_view) + + self.props.width = style.GRID_CELL_SIZE + self.props.height = style.GRID_CELL_SIZE + self.props.size = style.SMALL_ICON_SIZE + self.props.icon_name = 'school-server' + self.props.mode = Gtk.CellRendererMode.ACTIVATABLE + client = GConf.Client.get_default() + prelit_color = XoColor(client.get_string('/desktop/sugar/user/color')) + self.props.prelit_stroke_color = prelit_color.get_stroke_color() + self.props.prelit_fill_color = prelit_color.get_fill_color() + + class CellRendererActivityIcon(CellRendererIcon): __gtype_name__ = 'SugarCellRendererActivityIcon' @@ -500,18 +544,18 @@ class ActivityListPalette(ActivityPalette): self._favorite_item.set_image(self._favorite_icon) self._favorite_item.connect('activate', self.__change_favorite_activate_cb) - self.menu.append(self._favorite_item) +# self.menu.append(self._favorite_item) self._favorite_item.show() if activity_info.is_user_activity(): - self._add_erase_option(registry, activity_info) + pass # self._add_erase_option(registry, activity_info) registry = bundleregistry.get_registry() self._activity_changed_sid = registry.connect('bundle_changed', self.__activity_changed_cb) self._update_favorite_item() - self.menu.connect('destroy', self.__destroy_cb) + #self.menu.connect('destroy', self.__destroy_cb) def _add_erase_option(self, registry, activity_info): menu_item = MenuItem(_('Erase'), 'list-remove') @@ -556,3 +600,4 @@ class ActivityListPalette(ActivityPalette): def __erase_activate_cb(self, menu_item): self.emit('erase-activated', self._bundle_id) + diff --git a/src/jarabe/desktop/favoritesview.py b/src/jarabe/desktop/favoritesview.py index a9358f6..16a0cef 100644 --- a/src/jarabe/desktop/favoritesview.py +++ b/src/jarabe/desktop/favoritesview.py @@ -74,8 +74,13 @@ _favorites_settings = None class FavoritesBox(Gtk.VBox): __gtype_name__ = 'SugarFavoritesBox' - def __init__(self): + def __init__(self, favorites): + """ + If the argument is True, it will load the activities marked as favorites. + Else, it will load the activities marked for school. + """ Gtk.VBox.__init__(self) + self.load_favorites = favorites self._view = FavoritesView(self) self.pack_start(self._view, True, True, 0) @@ -157,9 +162,14 @@ class FavoritesView(ViewContainer): self.set_layout(self._layout) registry = bundleregistry.get_registry() for info in registry: - if registry.is_bundle_favorite(info.get_bundle_id(), - info.get_activity_version()): - self._add_activity(info) + if self._box.load_favorites: + if registry.is_bundle_favorite(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) + else: + if registry.is_bundle_for_school(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) def _set_layout(self, layout): if layout not in LAYOUT_MAP: @@ -288,11 +298,16 @@ class FavoritesView(ViewContainer): def __connect_to_bundle_registry_cb(self): registry = bundleregistry.get_registry() - for info in registry: - if registry.is_bundle_favorite(info.get_bundle_id(), - info.get_activity_version()): - self._add_activity(info) - + if self._box.load_favorites: + for info in registry: + if registry.is_bundle_favorite(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) + else: + for info in registry: + if registry.is_bundle_for_school(info.get_bundle_id(), + info.get_activity_version()): + self._add_activity(info) registry.connect('bundle-added', self.__activity_added_cb) registry.connect('bundle-removed', self.__activity_removed_cb) registry.connect('bundle-changed', self.__activity_changed_cb) @@ -308,9 +323,14 @@ class FavoritesView(ViewContainer): def __activity_added_cb(self, activity_registry, activity_info): registry = bundleregistry.get_registry() - if registry.is_bundle_favorite(activity_info.get_bundle_id(), - activity_info.get_activity_version()): - self._add_activity(activity_info) + if self._box.load_favorites: + if registry.is_bundle_favorite(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) + else: + if registry.is_bundle_for_school(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) def __activity_removed_cb(self, activity_registry, activity_info): icon = self._find_activity_icon(activity_info.get_bundle_id(), @@ -334,9 +354,14 @@ class FavoritesView(ViewContainer): self.remove(icon) registry = bundleregistry.get_registry() - if registry.is_bundle_favorite(activity_info.get_bundle_id(), - activity_info.get_activity_version()): - self._add_activity(activity_info) + if self._box.load_favorites: + if registry.is_bundle_favorite(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) + else: + if registry.is_bundle_for_school(activity_info.get_bundle_id(), + activity_info.get_activity_version()): + self._add_activity(activity_info) def set_filter(self, query): query = query.strip() diff --git a/src/jarabe/desktop/homebox.py b/src/jarabe/desktop/homebox.py index b87c345..2af711f 100644 --- a/src/jarabe/desktop/homebox.py +++ b/src/jarabe/desktop/homebox.py @@ -1,4 +1,5 @@ # Copyright (C) 2008 One Laptop Per Child +# Copyright (C) 2012 Daniel Francis # # 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 @@ -30,7 +31,8 @@ from jarabe.desktop.activitieslist import ActivitiesList from jarabe.util.normalize import normalize_string _FAVORITES_VIEW = 0 -_LIST_VIEW = 1 +_SCHOOL_VIEW = 1 +_LIST_VIEW = 2 class HomeBox(Gtk.VBox): @@ -41,7 +43,8 @@ class HomeBox(Gtk.VBox): Gtk.VBox.__init__(self) - self._favorites_box = favoritesview.FavoritesBox() + self._favorites_box = favoritesview.FavoritesBox(True) + self._school_box = favoritesview.FavoritesBox(False) self._list_view = ActivitiesList() toolbar.connect('query-changed', self.__toolbar_query_changed_cb) @@ -81,8 +84,10 @@ class HomeBox(Gtk.VBox): def __software_update_response_cb(self, alert, response_id): if self._list_view in self.get_children(): self._list_view.remove_alert() - else: + elif self._favorites_box in self.get_children(): self._favorites_box.remove_alert() + else: + self._school_box.remove_alert() if response_id != Gtk.ResponseType.REJECT: update_trigger_file = os.path.expanduser('~/.sugar-update') @@ -104,6 +109,7 @@ class HomeBox(Gtk.VBox): self._query = normalize_string(query.decode('utf-8')) self._list_view.set_filter(self._query) self._favorites_box.set_filter(self._query) + self._school_box.set_filter(self._query) def __toolbar_view_changed_cb(self, toolbar, view): self._set_view(view) @@ -126,14 +132,30 @@ class HomeBox(Gtk.VBox): if view == _FAVORITES_VIEW: if self._list_view in self.get_children(): self.remove(self._list_view) + elif self._school_box in self.get_children(): + self.remove(self._school_box) if self._favorites_box not in self.get_children(): self.add(self._favorites_box) self._favorites_box.show() self._favorites_box.grab_focus() + + elif view == _SCHOOL_VIEW: + if self._list_view in self.get_children(): + self.remove(self._list_view) + elif self._favorites_box in self.get_children(): + self.remove(self._favorites_box) + + if self._school_box not in self.get_children(): + self.add(self._school_box) + self._school_box.show() + self._school_box.grab_focus() + elif view == _LIST_VIEW: if self._favorites_box in self.get_children(): self.remove(self._favorites_box) + elif self._school_box in self.get_children(): + self.remove(self._school_box) if self._list_view not in self.get_children(): self.add(self._list_view) @@ -160,3 +182,4 @@ class HomeBox(Gtk.VBox): if resume_mode and self._query != '': self._list_view.set_filter(self._query) self._favorites_box.set_filter(self._query) + self._school_box.set_filter(self._query) diff --git a/src/jarabe/desktop/viewtoolbar.py b/src/jarabe/desktop/viewtoolbar.py index 5cb0186..8d658d8 100644 --- a/src/jarabe/desktop/viewtoolbar.py +++ b/src/jarabe/desktop/viewtoolbar.py @@ -2,6 +2,7 @@ # Copyright (C) 2009 Tomeu Vizoso, Simon Schampijer # Copyright (C) 2009-2012 One Laptop per Child # Copyright (C) 2010 Collabora Ltd. +# Copyright (C) 2012 Daniel Francis # # 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 @@ -31,7 +32,8 @@ from jarabe.desktop import favoritesview _AUTOSEARCH_TIMEOUT = 1000 _FAVORITES_VIEW = 0 -_LIST_VIEW = 1 +_SCHOOL_VIEW = 1 +_LIST_VIEW = 2 class ViewToolbar(Gtk.Toolbar): @@ -75,6 +77,13 @@ class ViewToolbar(Gtk.Toolbar): _FAVORITES_VIEW) self.insert(self._favorites_button, -1) + self._school_button = SchoolButton() + self._school_button.props.group = self._favorites_button + self._school_button.connect('toggled', + self.__view_button_toggled_cb, + _SCHOOL_VIEW) + self.insert(self._school_button, -1) + self._list_button = RadioToolButton(icon_name='view-list') self._list_button.props.group = self._favorites_button self._list_button.props.tooltip = _('List view') @@ -87,10 +96,12 @@ class ViewToolbar(Gtk.Toolbar): def show_view_buttons(self): self._favorites_button.show() + self._school_button.show() self._list_button.show() def hide_view_buttons(self): self._favorites_button.hide() + self._school_button.hide() self._list_button.hide() def clear_query(self): @@ -149,6 +160,58 @@ class FavoritesButton(RadioToolButton): self.props.tooltip = _('Favorites view') self.props.accelerator = _('1') self.props.group = None + self.props.icon_name = 'gtk-home' + + favorites_settings = favoritesview.get_settings() + self._layout = favorites_settings.layout + self._update_icon() + + # someday, this will be a Gtk.Table() + layouts_grid = Gtk.HBox() + layout_item = None + for layoutid, layoutclass in sorted(favoritesview.LAYOUT_MAP.items()): + layout_item = RadioToolButton(icon_name=layoutclass.icon_name, + group=layout_item, active=False) + if layoutid == self._layout: + layout_item.set_active(True) + layouts_grid.pack_start(layout_item, True, False, 0) + layout_item.connect('toggled', self.__layout_activate_cb, + layoutid) + layouts_grid.show_all() + self.props.palette.set_content(layouts_grid) + + def __layout_activate_cb(self, menu_item, layout): + if not menu_item.get_active(): + return + if self._layout == layout and self.props.active: + return + + if self._layout != layout: + self._layout = layout + self._update_icon() + + favorites_settings = favoritesview.get_settings() + favorites_settings.layout = layout + + if not self.props.active: + self.props.active = True + else: + self.emit('toggled') + + def _update_icon(self): + pass + # self.props.icon_name = favoritesview.LAYOUT_MAP[self._layout].icon_name + + +class SchoolButton(RadioToolButton): + __gtype_name__ = 'SugarSchoolButton' + + def __init__(self): + RadioToolButton.__init__(self) + self.props.icon_name = 'school-server' + self.props.tooltip = _('School favorites') + self.props.accelerator = _('I') + self.props.group = None favorites_settings = favoritesview.get_settings() self._layout = favorites_settings.layout @@ -187,4 +250,5 @@ class FavoritesButton(RadioToolButton): self.emit('toggled') def _update_icon(self): - self.props.icon_name = favoritesview.LAYOUT_MAP[self._layout].icon_name + pass + #self.props.icon_name = favoritesview.LAYOUT_MAP[self._layout].icon_name diff --git a/src/jarabe/model/bundleregistry.py b/src/jarabe/model/bundleregistry.py index e441122..fb133a6 100644 --- a/src/jarabe/model/bundleregistry.py +++ b/src/jarabe/model/bundleregistry.py @@ -1,5 +1,6 @@ # Copyright (C) 2006-2007 Red Hat, Inc. # Copyright (C) 2009 Aleksey Lim +# Copyright (C) 2012 Daniel Francis # # 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 @@ -70,7 +71,9 @@ class BundleRegistry(GObject.GObject): self._gio_monitors.append(monitor) self._last_defaults_mtime = -1 + self._last_school_defaults_mtime = -1 self._favorite_bundles = {} + self._school_bundles = {} client = GConf.Client.get_default() self._protected_activities = [] @@ -145,6 +148,27 @@ class BundleRegistry(GObject.GObject): self._last_defaults_mtime = float(favorites_data['defaults-mtime']) self._favorite_bundles = favorite_bundles + school_path = env.get_profile_path('school_activities') + if os.path.exists(school_path): + school_data = simplejson.load(open(school_path)) + + school_bundles = school_data['school'] + if not isinstance(school_bundles, dict): + raise ValueError('Invalid format in %s.' % school_path) + if school_bundles: + first_key = school_bundles.keys()[0] + if not isinstance(first_key, basestring): + raise ValueError('Invalid format in %s.' % school_path) + + first_value = school_bundles.values()[0] + if first_value is not None and \ + not isinstance(first_value, dict): + raise ValueError('Invalid format in %s.' % school_path) + + self._last_school_defaults_mtime = float(school_data['defaults-mtime']) + self._school_bundles = school_bundles + + def _merge_default_favorites(self): default_activities = [] defaults_path = os.path.join(config.data_path, 'activities.defaults') @@ -179,6 +203,40 @@ class BundleRegistry(GObject.GObject): self._write_favorites_file() + default_school_activities = [] + defaults_school_path = os.path.join(config.data_path, 'schoolactivities.defaults') + if os.path.exists(defaults_school_path): + file_mtime = os.stat(defaults_school_path).st_mtime + if file_mtime > self._last_school_defaults_mtime: + f = open(defaults_school_path, 'r') + for line in f.readlines(): + line = line.strip() + if line and not line.startswith('#'): + default_school_activities.append(line) + f.close() + self._last_school_defaults_mtime = file_mtime + + if not default_school_activities: + return + + for bundle_id in default_school_activities: + max_version = '0' + for bundle in self._bundles: + if bundle.get_bundle_id() == bundle_id and \ + NormalizedVersion(max_version) < \ + NormalizedVersion(bundle.get_activity_version()): + max_version = bundle.get_activity_version() + + key = self._get_favorite_key(bundle_id, max_version) + if NormalizedVersion(max_version) > NormalizedVersion('0') and \ + key not in self._favorite_bundles: + self._school_bundles[key] = None + + logging.debug('After merging: %r', self._favorite_bundles) + + self._write_school_file() + + def get_bundle(self, bundle_id): """Returns an bundle given his service name""" for bundle in self._bundles: @@ -314,10 +372,32 @@ class BundleRegistry(GObject.GObject): self._write_favorites_file() return True + def set_bundle_for_school(self, bundle_id, version, favorite): + changed = self._set_bundle_for_school(bundle_id, version, favorite) + if changed: + bundle = self._find_bundle(bundle_id, version) + self.emit('bundle-changed', bundle) + + def _set_bundle_for_school(self, bundle_id, version, favorite): + key = self._get_favorite_key(bundle_id, version) + if favorite and not key in self._school_bundles: + self._school_bundles[key] = None + elif not favorite and key in self._school_bundles: + del self._school_bundles[key] + else: + return False + + self._write_school_file() + return True + def is_bundle_favorite(self, bundle_id, version): key = self._get_favorite_key(bundle_id, version) return key in self._favorite_bundles + def is_bundle_for_school(self, bundle_id, version): + key = self._get_favorite_key(bundle_id, version) + return key in self._school_bundles + def is_activity_protected(self, bundle_id): return bundle_id in self._protected_activities @@ -357,6 +437,12 @@ class BundleRegistry(GObject.GObject): 'favorites': self._favorite_bundles} simplejson.dump(favorites_data, open(path, 'w'), indent=1) + def _write_school_file(self): + path = env.get_profile_path('school_activities') + school_data = {'defaults-mtime': self._last_school_defaults_mtime, + 'school': self._favorite_bundles} + simplejson.dump(school_data, open(path, 'w'), indent=1) + def is_installed(self, bundle): # TODO treat ContentBundle in special way # needs rethinking while fixing ContentBundle support -- cgit v0.9.1