Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/desktop/activitieslist.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/desktop/activitieslist.py')
-rw-r--r--src/jarabe/desktop/activitieslist.py461
1 files changed, 461 insertions, 0 deletions
diff --git a/src/jarabe/desktop/activitieslist.py b/src/jarabe/desktop/activitieslist.py
new file mode 100644
index 0000000..7bf0960
--- /dev/null
+++ b/src/jarabe/desktop/activitieslist.py
@@ -0,0 +1,461 @@
+# Copyright (C) 2008 One Laptop Per Child
+# Copyright (C) 2009 Tomeu Vizoso
+#
+# 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 gobject
+import pango
+import gconf
+import gtk
+
+from sugar import util
+from sugar.graphics import style
+from sugar.graphics.icon import Icon, CellRendererIcon
+from sugar.graphics.xocolor import XoColor
+from sugar.graphics.menuitem import MenuItem
+from sugar.graphics.alert import Alert
+
+from jarabe.model import bundleregistry
+from jarabe.view.palettes import ActivityPalette
+from jarabe.journal import misc
+
+
+class ActivitiesTreeView(gtk.TreeView):
+ __gtype_name__ = 'SugarActivitiesTreeView'
+
+ __gsignals__ = {
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
+ }
+
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ self._query = ''
+
+ self.modify_base(gtk.STATE_NORMAL, style.COLOR_WHITE.get_gdk_color())
+ self.set_headers_visible(False)
+ selection = self.get_selection()
+ selection.set_mode(gtk.SELECTION_NONE)
+
+ model = ListModel()
+ model.set_visible_func(self.__model_visible_cb)
+ self.set_model(model)
+
+ cell_favorite = CellRendererFavorite(self)
+ cell_favorite.connect('clicked', self.__favorite_clicked_cb)
+
+ column = gtk.TreeViewColumn()
+ column.pack_start(cell_favorite)
+ column.set_cell_data_func(cell_favorite, self.__favorite_set_data_cb)
+ self.append_column(column)
+
+ cell_icon = CellRendererActivityIcon(self)
+ cell_icon.connect('erase-activated', self.__erase_activated_cb)
+ cell_icon.connect('clicked', self.__icon_clicked_cb)
+
+ column = gtk.TreeViewColumn()
+ column.pack_start(cell_icon)
+ column.add_attribute(cell_icon, 'file-name', ListModel.COLUMN_ICON)
+ self.append_column(column)
+
+ cell_text = gtk.CellRendererText()
+ cell_text.props.ellipsize = pango.ELLIPSIZE_MIDDLE
+ cell_text.props.ellipsize_set = True
+
+ column = gtk.TreeViewColumn()
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_GROW_ONLY
+ column.props.expand = True
+ column.set_sort_column_id(ListModel.COLUMN_TITLE)
+ column.pack_start(cell_text)
+ column.add_attribute(cell_text, 'markup', ListModel.COLUMN_TITLE)
+ self.append_column(column)
+
+ cell_text = gtk.CellRendererText()
+ cell_text.props.xalign = 1
+
+ column = gtk.TreeViewColumn()
+ column.set_alignment(1)
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_GROW_ONLY
+ column.props.resizable = True
+ column.props.reorderable = True
+ column.props.expand = True
+ column.set_sort_column_id(ListModel.COLUMN_VERSION)
+ column.pack_start(cell_text)
+ column.add_attribute(cell_text, 'text', ListModel.COLUMN_VERSION_TEXT)
+ self.append_column(column)
+
+ cell_text = gtk.CellRendererText()
+ cell_text.props.xalign = 1
+
+ column = gtk.TreeViewColumn()
+ column.set_alignment(1)
+ column.props.sizing = gtk.TREE_VIEW_COLUMN_GROW_ONLY
+ column.props.resizable = True
+ column.props.reorderable = True
+ column.props.expand = True
+ column.set_sort_column_id(ListModel.COLUMN_DATE)
+ column.pack_start(cell_text)
+ column.add_attribute(cell_text, 'text', ListModel.COLUMN_DATE_TEXT)
+ self.append_column(column)
+
+ self.set_search_column(ListModel.COLUMN_TITLE)
+ self.set_enable_search(False)
+
+ def __erase_activated_cb(self, cell_renderer, bundle_id):
+ self.emit('erase-activated', bundle_id)
+
+ def __favorite_set_data_cb(self, column, cell, model, tree_iter):
+ favorite = model[tree_iter][ListModel.COLUMN_FAVORITE]
+ if favorite:
+ 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()
+ registry.set_bundle_favorite(row[ListModel.COLUMN_BUNDLE_ID],
+ row[ListModel.COLUMN_VERSION],
+ not row[ListModel.COLUMN_FAVORITE])
+
+ def __icon_clicked_cb(self, cell, path):
+ row = self.get_model()[path]
+
+ registry = bundleregistry.get_registry()
+ bundle = registry.get_bundle(row[ListModel.COLUMN_BUNDLE_ID])
+
+ misc.launch(bundle)
+
+ def set_filter(self, query):
+ self._query = query.lower()
+ self.get_model().refilter()
+
+ def __model_visible_cb(self, model, tree_iter):
+ title = model[tree_iter][ListModel.COLUMN_TITLE]
+ return title is not None and title.lower().find(self._query) > -1
+
+
+class ListModel(gtk.TreeModelSort):
+ __gtype_name__ = 'SugarListModel'
+
+ 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
+
+ def __init__(self):
+ self._model = gtk.ListStore(str, bool, str, str, str, str, int, str)
+ self._model_filter = self._model.filter_new()
+ gtk.TreeModelSort.__init__(self, self._model_filter)
+
+ gobject.idle_add(self.__connect_to_bundle_registry_cb)
+
+ def __connect_to_bundle_registry_cb(self):
+ registry = bundleregistry.get_registry()
+ for info in registry:
+ self._add_activity(info)
+ registry.connect('bundle-added', self.__activity_added_cb)
+ registry.connect('bundle-changed', self.__activity_changed_cb)
+ registry.connect('bundle-removed', self.__activity_removed_cb)
+
+ def __activity_added_cb(self, activity_registry, activity_info):
+ self._add_activity(activity_info)
+
+ def __activity_changed_cb(self, activity_registry, activity_info):
+ bundle_id = activity_info.get_bundle_id()
+ version = activity_info.get_activity_version()
+ favorite = activity_registry.is_bundle_favorite(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
+ return
+
+ def __activity_removed_cb(self, activity_registry, activity_info):
+ bundle_id = activity_info.get_bundle_id()
+ version = activity_info.get_activity_version()
+ for row in self._model:
+ if row[ListModel.COLUMN_BUNDLE_ID] == bundle_id and \
+ row[ListModel.COLUMN_VERSION] == version:
+ self._model.remove(row.iter)
+ return
+
+ def _add_activity(self, activity_info):
+ if activity_info.get_bundle_id() == 'org.laptop.JournalActivity':
+ return
+
+ timestamp = activity_info.get_installation_time()
+ version = activity_info.get_activity_version()
+
+ registry = bundleregistry.get_registry()
+ favorite = registry.is_bundle_favorite(activity_info.get_bundle_id(),
+ version)
+
+ tag_list = activity_info.get_tags()
+ if tag_list is None or not tag_list:
+ title = '<b>%s</b>' % activity_info.get_name()
+ else:
+ tags = ', '.join(tag_list)
+ title = '<b>%s</b>\n' \
+ '<span style="italic" weight="light">%s</span>' % \
+ (activity_info.get_name(), tags)
+
+ self._model.append([activity_info.get_bundle_id(),
+ favorite,
+ activity_info.get_icon(),
+ title,
+ version,
+ _('Version %s') % version,
+ timestamp,
+ util.timestamp_to_elapsed_string(timestamp)])
+
+ def set_visible_func(self, func):
+ self._model_filter.set_visible_func(func)
+
+ def refilter(self):
+ self._model_filter.refilter()
+
+
+class CellRendererFavorite(CellRendererIcon):
+ __gtype_name__ = 'SugarCellRendererFavorite'
+
+ 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 = 'emblem-favorite'
+ self.props.mode = gtk.CELL_RENDERER_MODE_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'
+
+ __gsignals__ = {
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
+ }
+
+ 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.STANDARD_ICON_SIZE
+ self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
+ self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
+ self.props.mode = gtk.CELL_RENDERER_MODE_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()
+
+ self._tree_view = tree_view
+
+ def create_palette(self):
+ model = self._tree_view.get_model()
+ row = model[self.props.palette_invoker.path]
+ bundle_id = row[ListModel.COLUMN_BUNDLE_ID]
+
+ registry = bundleregistry.get_registry()
+ palette = ActivityListPalette(registry.get_bundle(bundle_id))
+ palette.connect('erase-activated', self.__erase_activated_cb)
+ return palette
+
+ def __erase_activated_cb(self, palette, bundle_id):
+ self.emit('erase-activated', bundle_id)
+
+
+class ActivitiesList(gtk.VBox):
+ __gtype_name__ = 'SugarActivitiesList'
+
+ def __init__(self):
+ logging.debug('STARTUP: Loading the activities list')
+
+ gobject.GObject.__init__(self)
+
+ scrolled_window = gtk.ScrolledWindow()
+ scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+ scrolled_window.set_shadow_type(gtk.SHADOW_NONE)
+ scrolled_window.connect('key-press-event', self.__key_press_event_cb)
+ self.pack_start(scrolled_window)
+ scrolled_window.show()
+
+ self._tree_view = ActivitiesTreeView()
+ self._tree_view.connect('erase-activated', self.__erase_activated_cb)
+ scrolled_window.add(self._tree_view)
+ self._tree_view.show()
+
+ self._alert = None
+
+ def set_filter(self, query):
+ self._tree_view.set_filter(query)
+
+ def __key_press_event_cb(self, scrolled_window, event):
+ keyname = gtk.gdk.keyval_name(event.keyval)
+
+ vadjustment = scrolled_window.props.vadjustment
+ if keyname == 'Up':
+ if vadjustment.props.value > vadjustment.props.lower:
+ vadjustment.props.value -= vadjustment.props.step_increment
+ elif keyname == 'Down':
+ max_value = vadjustment.props.upper - vadjustment.props.page_size
+ if vadjustment.props.value < max_value:
+ vadjustment.props.value = min(
+ vadjustment.props.value + vadjustment.props.step_increment,
+ max_value)
+ else:
+ return False
+
+ return True
+
+ def add_alert(self, alert):
+ if self._alert is not None:
+ self.remove_alert()
+ self._alert = alert
+ self.pack_start(alert, False)
+ self.reorder_child(alert, 0)
+
+ def remove_alert(self):
+ self.remove(self._alert)
+ self._alert = None
+
+ def __erase_activated_cb(self, tree_view, bundle_id):
+ registry = bundleregistry.get_registry()
+ activity_info = registry.get_bundle(bundle_id)
+
+ alert = Alert()
+ alert.props.title = _('Confirm erase')
+ alert.props.msg = \
+ _('Confirm erase: Do you want to permanently erase %s?') \
+ % activity_info.get_name()
+
+ cancel_icon = Icon(icon_name='dialog-cancel')
+ alert.add_button(gtk.RESPONSE_CANCEL, _('Keep'), cancel_icon)
+
+ erase_icon = Icon(icon_name='dialog-ok')
+ alert.add_button(gtk.RESPONSE_OK, _('Erase'), erase_icon)
+
+ alert.connect('response', self.__erase_confirmation_dialog_response_cb,
+ bundle_id)
+
+ self.add_alert(alert)
+
+ def __erase_confirmation_dialog_response_cb(self, alert, response_id,
+ bundle_id):
+ self.remove_alert()
+ if response_id == gtk.RESPONSE_OK:
+ registry = bundleregistry.get_registry()
+ bundle = registry.get_bundle(bundle_id)
+ registry.uninstall(bundle, delete_profile=True)
+
+
+class ActivityListPalette(ActivityPalette):
+ __gtype_name__ = 'SugarActivityListPalette'
+
+ __gsignals__ = {
+ 'erase-activated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([str])),
+ }
+
+ def __init__(self, activity_info):
+ ActivityPalette.__init__(self, activity_info)
+
+ self._bundle_id = activity_info.get_bundle_id()
+ self._version = activity_info.get_activity_version()
+
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+
+ self._favorite_item = MenuItem('')
+ self._favorite_icon = Icon(icon_name='emblem-favorite',
+ icon_size=gtk.ICON_SIZE_MENU)
+ 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._favorite_item.show()
+
+ if activity_info.is_user_activity():
+ 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.connect('destroy', self.__destroy_cb)
+
+ def _add_erase_option(self, registry, activity_info):
+ menu_item = MenuItem(_('Erase'), 'list-remove')
+ menu_item.connect('activate', self.__erase_activate_cb)
+ self.menu.append(menu_item)
+ menu_item.show()
+
+ if not os.access(activity_info.get_path(), os.W_OK) or \
+ registry.is_activity_protected(self._bundle_id):
+ menu_item.props.sensitive = False
+
+ def __destroy_cb(self, palette):
+ registry = bundleregistry.get_registry()
+ registry.disconnect(self._activity_changed_sid)
+
+ def _update_favorite_item(self):
+ label = self._favorite_item.child
+ if self._favorite:
+ label.set_text(_('Remove favorite'))
+ xo_color = XoColor('%s,%s' % (style.COLOR_WHITE.get_svg(),
+ style.COLOR_TRANSPARENT.get_svg()))
+ else:
+ label.set_text(_('Make favorite'))
+ client = gconf.client_get_default()
+ xo_color = XoColor(client.get_string('/desktop/sugar/user/color'))
+
+ self._favorite_icon.props.xo_color = xo_color
+
+ def __change_favorite_activate_cb(self, menu_item):
+ registry = bundleregistry.get_registry()
+ registry.set_bundle_favorite(self._bundle_id,
+ self._version,
+ not self._favorite)
+
+ def __activity_changed_cb(self, activity_registry, activity_info):
+ if activity_info.get_bundle_id() == self._bundle_id and \
+ activity_info.get_activity_version() == self._version:
+ registry = bundleregistry.get_registry()
+ self._favorite = registry.is_bundle_favorite(self._bundle_id,
+ self._version)
+ self._update_favorite_item()
+
+ def __erase_activate_cb(self, menu_item):
+ self.emit('erase-activated', self._bundle_id)