From 26ed1d150475f82a68cfac45f3698cde0b6a082c Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 17 Apr 2007 13:27:34 -0400 Subject: [PATCH 1/6] Don't leak invalid properties --- services/presence/buddy.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index 8b2eec4a..fd01ce8e 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -205,31 +205,35 @@ class Buddy(DBusGObject): def set_properties(self, properties): changed = False + changed_props = {} if "nick" in properties.keys(): nick = properties["nick"] if nick != self._nick: self._nick = nick + changed_props["nick"] = nick changed = True if "color" in properties.keys(): color = properties["color"] if color != self._color: self._color = color + changed_props["color"] = color changed = True if "current-activity" in properties.keys(): curact = properties["current-activity"] if curact != self._current_activity: self._current_activity = curact + changed_props["current-activity"] = curact changed = True - if not changed: + if not changed or not len(changed_props.keys()): return # Try emitting PropertyChanged before updating validity # to avoid leaking a PropertyChanged signal before the buddy is # actually valid the first time after creation if self._valid: - self.PropertyChanged(properties) - self.emit('property-changed', properties) + self.PropertyChanged(changed_props) + self.emit('property-changed', changed_props) self._update_validity() From 37953c47f75a197aac1fb07cf8a3d177f9674610 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Apr 2007 12:34:16 -0400 Subject: [PATCH 2/6] Use well-defined names for properties rather than strings --- services/presence/buddy.py | 92 ++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 39 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index fd01ce8e..0f4fef48 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -37,6 +37,14 @@ class DBusGObjectMetaclass(dbus.service.InterfaceType, gobject.GObjectMeta): pas class DBusGObject(dbus.service.Object, gobject.GObject): __metaclass__ = DBusGObjectMetaclass +_PROP_NICK = "nick" +_PROP_KEY = "key" +_PROP_ICON = "icon" +_PROP_CURACT = "current-activity" +_PROP_COLOR = "color" +_PROP_OWNER = "owner" +_PROP_VALID = "valid" + class Buddy(DBusGObject): """Represents another person on the network and keeps track of the activities and resources they make available for sharing.""" @@ -51,14 +59,14 @@ class Buddy(DBusGObject): } __gproperties__ = { - 'key' : (str, None, None, None, + _PROP_KEY : (str, None, None, None, gobject.PARAM_READWRITE | gobject.PARAM_CONSTRUCT_ONLY), - 'icon' : (object, None, None, gobject.PARAM_READWRITE), - 'nick' : (str, None, None, None, gobject.PARAM_READWRITE), - 'color' : (str, None, None, None, gobject.PARAM_READWRITE), - 'current-activity' : (str, None, None, None, gobject.PARAM_READWRITE), - 'valid' : (bool, None, None, False, gobject.PARAM_READABLE), - 'owner' : (bool, None, None, False, gobject.PARAM_READABLE) + _PROP_ICON : (object, None, None, gobject.PARAM_READWRITE), + _PROP_NICK : (str, None, None, None, gobject.PARAM_READWRITE), + _PROP_COLOR : (str, None, None, None, gobject.PARAM_READWRITE), + _PROP_CURACT : (str, None, None, None, gobject.PARAM_READWRITE), + _PROP_VALID : (bool, None, None, False, gobject.PARAM_READABLE), + _PROP_OWNER : (bool, None, None, False, gobject.PARAM_READABLE) } def __init__(self, bus_name, object_id, **kwargs): @@ -86,41 +94,47 @@ class Buddy(DBusGObject): if not kwargs.get("key"): raise ValueError("key required") + _ALLOWED_INIT_PROPS = [_PROP_NICK, _PROP_KEY, _PROP_ICON, _PROP_CURACT, _PROP_COLOR] + for (key, value) in kwargs.items(): + if key not in _ALLOWED_INIT_PROPS: + logging.debug("Invalid init property '%s'; ignoring..." % key) + del kwargs[key] + gobject.GObject.__init__(self, **kwargs) def do_get_property(self, pspec): - if pspec.name == "key": + if pspec.name == _PROP_KEY: return self._key - elif pspec.name == "icon": + elif pspec.name == _PROP_ICON: return self._icon - elif pspec.name == "nick": + elif pspec.name == _PROP_NICK: return self._nick - elif pspec.name == "color": + elif pspec.name == _PROP_COLOR: return self._color - elif pspec.name == "current-activity": + elif pspec.name == _PROP_CURACT: if not self._current_activity: return None if not self._activities.has_key(self._current_activity): return None return self._current_activity - elif pspec.name == "valid": + elif pspec.name == _PROP_VALID: return self._valid - elif pspec.name == "owner": + elif pspec.name == _PROP_OWNER: return self._owner def do_set_property(self, pspec, value): - if pspec.name == "icon": + if pspec.name == _PROP_ICON: if str(value) != self._icon: self._icon = str(value) self.IconChanged(self._icon) self.emit('icon-changed', self._icon) - elif pspec.name == "nick": + elif pspec.name == _PROP_NICK: self._nick = value - elif pspec.name == "color": + elif pspec.name == _PROP_COLOR: self._color = value - elif pspec.name == "current-activity": + elif pspec.name == _PROP_CURACT: self._current_activity = value - elif pspec.name == "key": + elif pspec.name == _PROP_KEY: self._key = value self._update_validity() @@ -166,14 +180,14 @@ class Buddy(DBusGObject): in_signature="", out_signature="a{sv}") def GetProperties(self): props = {} - props['nick'] = self.props.nick - props['owner'] = self.props.owner - props['key'] = self.props.key - props['color'] = self.props.color + props[_PROP_NICK] = self.props.nick + props[_PROP_OWNER] = self.props.owner + props[_PROP_KEY] = self.props.key + props[_PROP_COLOR] = self.props.color if self.props.current_activity: - props['current-activity'] = self.props.current_activity + props[_PROP_CURACT] = self.props.current_activity else: - props['current-activity'] = "" + props[_PROP_CURACT] = "" return props # methods @@ -206,23 +220,23 @@ class Buddy(DBusGObject): def set_properties(self, properties): changed = False changed_props = {} - if "nick" in properties.keys(): - nick = properties["nick"] + if _PROP_NICK in properties.keys(): + nick = properties[_PROP_NICK] if nick != self._nick: self._nick = nick - changed_props["nick"] = nick + changed_props[_PROP_NICK] = nick changed = True - if "color" in properties.keys(): - color = properties["color"] + if _PROP_COLOR in properties.keys(): + color = properties[_PROP_COLOR] if color != self._color: self._color = color - changed_props["color"] = color + changed_props[_PROP_COLOR] = color changed = True - if "current-activity" in properties.keys(): - curact = properties["current-activity"] + if _PROP_CURACT in properties.keys(): + curact = properties[_PROP_CURACT] if curact != self._current_activity: self._current_activity = curact - changed_props["current-activity"] = curact + changed_props[_PROP_CURACT] = curact changed = True if not changed or not len(changed_props.keys()): @@ -355,11 +369,11 @@ class ShellOwner(GenericOwner): self.props.icon = icon def _color_changed_cb(self, color): - props = {'color': color} + props = {_PROP_COLOR: color} self.set_properties(props) def _nick_changed_cb(self, nick): - props = {'nick': nick} + props = {_PROP_NICK: nick} self.set_properties(props) def _cur_activity_changed_cb(self, activity_id): @@ -480,10 +494,10 @@ class TestOwner(GenericOwner): self.props.icon = _get_random_image() elif it == 1: from sugar.graphics import xocolor - props = {'color': xocolor.XoColor().to_string()} + props = {_PROP_COLOR: xocolor.XoColor().to_string()} self.set_properties(props) elif it == 2: - props = {'nick': _get_random_name()} + props = {_PROP_NICK: _get_random_name()} self.set_properties(props) elif it == 3: actid = "" @@ -493,7 +507,7 @@ class TestOwner(GenericOwner): if idx < len(self._test_activities): activity = self._test_activities[idx] actid = activity.props.id - props = {'current-activity': actid} + props = {_PROP_CURACT: actid} self.set_properties(props) return True From e46d31e0f4c1020d4e4d8e3d5f13488a8421ae30 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Apr 2007 12:38:04 -0400 Subject: [PATCH 3/6] Fix current-activity-changed signals in BuddyModel --- shell/model/BuddyModel.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/shell/model/BuddyModel.py b/shell/model/BuddyModel.py index 2339832f..a551e1e0 100644 --- a/shell/model/BuddyModel.py +++ b/shell/model/BuddyModel.py @@ -130,6 +130,8 @@ class BuddyModel(gobject.GObject): if 'color' in keys: self._set_color_from_string(self._buddy.props.color) self.emit('color-changed', self.get_color()) + if 'current-activity' in keys: + self.emit('current-activity-changed', buddy.props.current_activity) def _buddy_disappeared_cb(self, buddy): if buddy != self._buddy: @@ -144,8 +146,3 @@ class BuddyModel(gobject.GObject): def _buddy_icon_changed_cb(self, buddy): self.emit('icon-changed') - - def _buddy_current_activity_changed_cb(self, buddy, activity=None): - if not self._buddy: - return - self.emit('current-activity-changed', activity) From 2866d3c4c92b2443cab3bb64c147c45f6909f47d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Apr 2007 12:40:53 -0400 Subject: [PATCH 4/6] Convert more string-named properties to var-named --- services/presence/buddy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/services/presence/buddy.py b/services/presence/buddy.py index 0f4fef48..0be877cb 100644 --- a/services/presence/buddy.py +++ b/services/presence/buddy.py @@ -91,7 +91,7 @@ class Buddy(DBusGObject): self._nick = None self._color = None - if not kwargs.get("key"): + if not kwargs.get(_PROP_KEY): raise ValueError("key required") _ALLOWED_INIT_PROPS = [_PROP_NICK, _PROP_KEY, _PROP_ICON, _PROP_CURACT, _PROP_COLOR] @@ -380,7 +380,7 @@ class ShellOwner(GenericOwner): if not self._activities.has_key(activity_id): # This activity is local-only activity_id = None - props = {'current-activity': activity_id} + props = {_PROP_CURACT: activity_id} self.set_properties(props) From d450ea2dd67279943fd570be8c9391aa95afb984 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Fri, 20 Apr 2007 13:56:46 -0400 Subject: [PATCH 5/6] Clean up buddy activity changed signals in MeshModel --- shell/model/MeshModel.py | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/shell/model/MeshModel.py b/shell/model/MeshModel.py index dd25d9f4..b037bb71 100644 --- a/shell/model/MeshModel.py +++ b/shell/model/MeshModel.py @@ -160,19 +160,14 @@ class MeshModel(gobject.GObject): def get_buddies(self): return self._buddies.values() - def _buddy_activity_changed_cb(self, buddy, cur_activity): - if not self._buddies.has_key(buddy.props.key): + def _buddy_activity_changed_cb(self, model, cur_activity): + if not self._buddies.has_key(model.get_key()): return - buddy_model = self._buddies[buddy.props.key] - if cur_activity == None: - self.emit('buddy-moved', buddy_model, None) - else: - self._notify_buddy_change(buddy_model, cur_activity) - - def _notify_buddy_change(self, buddy_model, cur_activity): - if self._activities.has_key(cur_activity.get_id()): + if cur_activity and self._activities.has_key(cur_activity.get_id()): activity_model = self._activities[cur_activity.get_id()] - self.emit('buddy-moved', buddy_model, activity_model) + self.emit('buddy-moved', model, activity_model) + else: + self.emit('buddy-moved', model, None) def _buddy_appeared_cb(self, pservice, buddy): if self._buddies.has_key(buddy.props.key): @@ -186,7 +181,7 @@ class MeshModel(gobject.GObject): cur_activity = buddy.props.current_activity if cur_activity: - self._notify_buddy_change(model, cur_activity) + self._buddy_activity_changed_cb(model, cur_activity) def _buddy_disappeared_cb(self, pservice, buddy): if not self._buddies.has_key(buddy.props.key): From ec8bb2193673847dace2ac49170c5961a032b056 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 20 Apr 2007 20:12:49 +0200 Subject: [PATCH 6/6] Implement installation of downloaded activities. --- shell/view/clipboardicon.py | 56 +++++++++++++++++++++++++++---------- shell/view/clipboardmenu.py | 14 +++++----- 2 files changed, 49 insertions(+), 21 deletions(-) diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index ef0de29e..143d016e 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -17,6 +17,7 @@ import logging import os +import urlparse import gobject @@ -74,8 +75,13 @@ class ClipboardIcon(CanvasIcon): self.props.background_color = color.TOOLBAR_BACKGROUND.get_int() def get_popup(self): + cb_service = clipboardservice.get_instance() + obj = cb_service.get_object(self._object_id) + formats = obj['FORMATS'] + self._menu = ClipboardMenu(self._name, self._percent, self._preview, - self._activity) + self._activity, + formats[0] == 'application/vnd.olpc-x-sugar') self._menu.connect('action', self._popup_action_cb) return self._menu @@ -83,15 +89,19 @@ class ClipboardIcon(CanvasIcon): return self._popup_context def set_state(self, name, percent, icon_name, preview, activity): + cb_service = clipboardservice.get_instance() + obj = cb_service.get_object(self._object_id) + installable = (obj['FORMATS'][0] == 'application/vnd.olpc-x-sugar') + self._name = name self._percent = percent self._preview = preview self._activity = activity self.set_property("icon_name", icon_name) if self._menu: - self._menu.set_state(name, percent, preview, activity) + self._menu.set_state(name, percent, preview, activity, installable) - if activity and percent < 100: + if (activity or installable) and percent < 100: self.props.xo_color = XoColor("#000000,#424242") else: self.props.xo_color = XoColor("#000000,#FFFFFF") @@ -107,27 +117,39 @@ class ClipboardIcon(CanvasIcon): self._open_file() def _open_file(self): - if self._percent < 100 or not self._activity: + if self._percent < 100: return # Get the file path cb_service = clipboardservice.get_instance() obj = cb_service.get_object(self._object_id) formats = obj['FORMATS'] - if len(formats) > 0: - path = cb_service.get_object_data(self._object_id, formats[0]) + if len(formats) == 0: + return - # FIXME: would be better to check for format.onDisk - try: - path_exists = os.path.exists(path) - except TypeError: - path_exists = False + if not self._activity and \ + not formats[0] == 'application/vnd.olpc-x-sugar': + return - if path_exists: - uri = 'file://' + path + uri = cb_service.get_object_data(self._object_id, formats[0]) + if not uri.startswith('file://'): + return + + path = urlparse.urlparse(uri).path + + # FIXME: would be better to check for format.onDisk + try: + path_exists = os.path.exists(path) + except TypeError: + path_exists = False + + if path_exists: + if self._activity: activityfactory.create_with_uri(self._activity, uri) else: - logging.debug("Clipboard item file path %s didn't exist" % path) + self._install_xo(path) + else: + logging.debug("Clipboard item file path %s didn't exist" % path) def _popup_action_cb(self, popup, menu_item): action = menu_item.props.action_id @@ -153,3 +175,9 @@ class ClipboardIcon(CanvasIcon): self.props.background_color = color.DESKTOP_BACKGROUND.get_int() else: self.props.background_color = color.TOOLBAR_BACKGROUND.get_int() + + def _install_xo(self, path): + logging.debug('mec') + if os.spawnlp(os.P_WAIT, 'sugar-install-bundle', 'sugar-install-bundle', + path): + raise RuntimeError, 'An error occurred while extracting the .xo contents.' diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py index 3e8239d6..9db69223 100644 --- a/shell/view/clipboardmenu.py +++ b/shell/view/clipboardmenu.py @@ -33,7 +33,7 @@ class ClipboardMenu(Menu): ACTION_OPEN = 1 ACTION_STOP_DOWNLOAD = 2 - def __init__(self, name, percent, preview, activity): + def __init__(self, name, percent, preview, activity, installable): Menu.__init__(self, name) self.props.border = 0 @@ -54,10 +54,10 @@ class ClipboardMenu(Menu): self._preview_text.props.font_desc = font.DEFAULT.get_pango_desc() self.append(self._preview_text) - self._update_icons(percent, activity) + self._update_icons(percent, activity, installable) - def _update_icons(self, percent, activity): - if percent == 100 and activity: + def _update_icons(self, percent, activity, installable): + if percent == 100 and (activity or installable): if not self._remove_item: self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, _('Remove'), @@ -73,7 +73,7 @@ class ClipboardMenu(Menu): if self._stop_item: self.remove_item(self._stop_item) self._stop_item = None - elif percent == 100 and not activity: + elif percent == 100 and (not activity and not installable): if not self._remove_item: self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, _('Remove'), @@ -102,8 +102,8 @@ class ClipboardMenu(Menu): self.remove_item(self._open_item) self._open_item = None - def set_state(self, name, percent, preview, activity): + def set_state(self, name, percent, preview, activity, installable): self.set_title(name) if self._progress_bar: self._progress_bar.set_property('percent', percent) - self._update_icons(percent, activity) + self._update_icons(percent, activity, installable)