From a7d625166480a67e95852a4c83b75423afa77c0e Mon Sep 17 00:00:00 2001 From: Eduardo Silva Date: Sun, 07 Jan 2007 17:25:25 +0000 Subject: Merge branch 'master' of git://dev.laptop.org/sugar --- (limited to 'shell') diff --git a/shell/model/homeactivity.py b/shell/model/homeactivity.py index 2143ef2..efaa2bb 100644 --- a/shell/model/homeactivity.py +++ b/shell/model/homeactivity.py @@ -14,23 +14,73 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +import time +import gobject +import logging + from sugar.presence import PresenceService from sugar.activity import Activity from sugar import profile -class HomeActivity: - def __init__(self, registry, window): +class HomeActivity(gobject.GObject): + __gsignals__ = { + 'launch-timeout': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([])), + } + + def __init__(self, bundle, activity_id): + gobject.GObject.__init__(self) + self._window = None + self._xid = None + self._service = None + self._id = activity_id + self._type = bundle.get_service_name() + self._icon_name = bundle.get_icon() + + self._launch_time = time.time() + self._launched = False + self._launch_timeout_id = gobject.timeout_add(10000, self._launch_timeout_cb) + + logging.debug("Activity %s (%s) launching..." % (self._id, self._type)) + + def __del__(self): + gobject.source_remove(self._launch_timeout_id) + self._launch_timeout_id = 0 + + def _launch_timeout_cb(self, user_data=None): + logging.debug("Activity %s (%s) launch timed out" % (self._id, self._type)) + self._launch_timeout_id = 0 + self.emit('launch-timeout') + return False + + def set_window(self, window): + """An activity is 'launched' once we get its window.""" + logging.debug("Activity %s (%s) finished launching" % (self._id, self._type)) + self._launched = True + gobject.source_remove(self._launch_timeout_id) + self._launch_timeout_id = 0 + + if self._window or self._xid: + raise RuntimeError("Activity is already launched!") + if not window: + raise ValueError("window must be valid") + self._window = window self._xid = window.get_xid() - self._service = Activity.get_service(window.get_xid()) - self._id = self._service.get_id() - self._type = self._service.get_type() - info = registry.get_bundle(self._type) - self._icon_name = info.get_icon() + # verify id and type details + act_id = self._service.get_id() + if act_id != self._id: + raise RuntimeError("Activity's real ID (%s) didn't match expected (%s)." % (act_id, self._id)) + act_type = self._service.get_type() + if act_type != self._type: + raise RuntimeError("Activity's real type (%s) didn't match expected (%s)." % (act_type, self._type)) def get_title(self): + if not self._launched: + raise RuntimeError("Activity is still launching.") return self._window.get_name() def get_icon_name(self): @@ -47,13 +97,25 @@ class HomeActivity: return self._id def get_xid(self): + if not self._launched: + raise RuntimeError("Activity is still launching.") return self._xid def get_window(self): + if not self._launched: + raise RuntimeError("Activity is still launching.") return self._window def get_type(self): return self._type def get_shared(self): + if not self._launched: + raise RuntimeError("Activity is still launching.") return self._service.get_shared() + + def get_launch_time(self): + return self._launch_time + + def get_launched(self): + return self._launched diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py index f4fd3ee..b1b4c8f 100644 --- a/shell/model/homemodel.py +++ b/shell/model/homemodel.py @@ -20,10 +20,14 @@ import gobject import wnck from model.homeactivity import HomeActivity +from sugar.activity import Activity class HomeModel(gobject.GObject): __gsignals__ = { + 'activity-launched': (gobject.SIGNAL_RUN_FIRST, + gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), 'activity-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), @@ -68,6 +72,12 @@ class HomeModel(gobject.GObject): if window.get_window_type() == wnck.WINDOW_NORMAL: self._remove_activity(window.get_xid()) + def _get_activity_by_xid(self, xid): + for act in self._activities.values(): + if act.get_xid() == xid: + return act + return None + def _active_window_changed_cb(self, screen): window = screen.get_active_window() if window == None: @@ -77,8 +87,13 @@ class HomeModel(gobject.GObject): return xid = window.get_xid() - if self._activities.has_key(xid): - self._current_activity = self._activities[xid] + act = self._get_activity_by_xid(window.get_xid()) + if act: + if act.get_launched() == True: + self._current_activity = act + else: + self._current_activity = None + logging.error('Actiivty for window %d was not yet launched.' % xid) else: self._current_activity = None logging.error('Model for window %d does not exist.' % xid) @@ -86,13 +101,57 @@ class HomeModel(gobject.GObject): self.emit('active-activity-changed', self._current_activity) def _add_activity(self, window): - activity = HomeActivity(self._bundle_registry, window) - self._activities[window.get_xid()] = activity + act_service = Activity.get_service(window.get_xid()) + act_id = act_service.get_id() + + activity = None + if self._activities.has_key(act_id): + activity = self._activities[act_id] + else: + # activity got lost, took longer to launch than we allow, + # or it was launched by something other than the shell + act_type = act_service.get_type() + bundle = self._bundle_registry.get_bundle(act_type) + if not bundle: + raise RuntimeError("No bundle for activity type '%s'." % act_type) + return + activity = HomeActivity(bundle, act_id) + self._activities[act_id] = activity + + activity.set_window(window) self.emit('activity-added', activity) + def _internal_remove_activity(self, activity): + self.emit('activity-removed', activity) + act_id = activity.get_id() + del self._activities[act_id] + def _remove_activity(self, xid): - if self._activities.has_key(xid): - self.emit('activity-removed', self._activities[xid]) - del self._activities[xid] + activity = self._get_activity_by_xid(xid) + if activity: + self._internal_remove_activity(activity) else: logging.error('Model for window %d does not exist.' % xid) + + def _activity_launch_timeout_cb(self, activity): + act_id = activity.get_id() + if not act_id in self._activities.keys(): + return + self._internal_remove_activity(activity) + + def notify_activity_launch(self, activity_id, service_name): + bundle = self._bundle_registry.get_bundle(service_name) + if not bundle: + raise ValueError("Activity service name '%s' was not found in the bundle registry." % service_name) + activity = HomeActivity(bundle, activity_id) + activity.connect('launch-timeout', self._activity_launch_timeout_cb) + self._activities[activity_id] = activity + self.emit('activity-launched', activity) + + def notify_activity_launch_failed(self, activity_id): + if self._activities.has_key(activity_id): + activity = self._activities[activity_id] + logging.debug("Activity %s (%s) launch failed" % (activity_id, activity.get_type())) + self._internal_remove_activity(activity) + else: + logging.error('Model for activity id %s does not exist.' % activity_id) diff --git a/shell/sugar-activity b/shell/sugar-activity index bb6cf30..43b56dc 100755 --- a/shell/sugar-activity +++ b/shell/sugar-activity @@ -18,9 +18,18 @@ import sys import os +import gobject from sugar.activity import ActivityFactory from sugar import env +from sugar import util + +def _success_cb(handler, activity, loop): + activity.start(util.unique_id()) + loop.quit() + +def _error_cb(handler, err, loop): + loop.quit() ppath = env.get_profile_path() bus_file = os.path.join(ppath, "session_bus_address") @@ -29,5 +38,10 @@ bus_name = f.read() f.close() os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_name -activity = ActivityFactory.create(sys.argv[1]) -activity.start() +loop = gobject.MainLoop() + +handler = ActivityFactory.create(sys.argv[1]) +handler.connect('success', _success_cb, loop) +handler.connect('error', _error_cb, loop) + +loop.run() diff --git a/shell/view/Shell.py b/shell/view/Shell.py index f3f6ca0..03a3bad 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -68,6 +68,8 @@ class Shell(gobject.GObject): self._frame = Frame(self) self._frame.show_and_hide(3) + self._pservice = PresenceService.get_instance() + #self.start_activity('org.laptop.JournalActivity') def _handle_camera_key(self): @@ -189,40 +191,94 @@ class Shell(gobject.GObject): def get_model(self): return self._model - def join_activity(self, bundle_id, activity_id): - pservice = PresenceService.get_instance() + def _join_success_cb(self, handler, activity, activity_ps, activity_id, activity_type): + logging.debug("Joining activity %s (%s)" % (activity_id, activity_type)) + activity.join(activity_ps.object_path()) + def _join_error_cb(self, handler, err, home_model, activity_id, activity_type): + logging.error("Couldn't launch activity %s (%s):\n%s" % (activity_id, activity_type, err)) + home_mode.notify_activity_launch_failed(activity_id) + + def join_activity(self, bundle_id, activity_id): activity = self.get_activity(activity_id) if activity: activity.present() - else: - activity_ps = pservice.get_activity(activity_id) - - if activity_ps: - # Get the service name for this activity, if - # we have a bundle on the system capable of handling - # this activity type - breg = self._model.get_bundle_registry() - bundle = breg.find_by_default_type(bundle_id) - if bundle: - serv_name = bundle.get_service_name() - try: - activity = ActivityFactory.create(serv_name) - except DBusException, e: - logging.error("Couldn't launch activity %s:\n%s" % (serv_name, e)) - else: - logging.debug("Joining activity type %s id %s" % (serv_name, activity_id)) - activity.join(activity_ps.object_path()) - else: - logging.error("Couldn't find activity for type %s" % bundle_id) - else: - logging.error('Cannot start activity.') + return + + activity_ps = self._pservice.get_activity(activity_id) + if not activity_ps: + logging.error("Couldn't find shared activity for %s" % activity_id) + return + + # Get the service name for this activity, if + # we have a bundle on the system capable of handling + # this activity type + breg = self._model.get_bundle_registry() + bundle = breg.find_by_default_type(bundle_id) + if not bundle: + logging.error("Couldn't find activity for type %s" % bundle_id) + return + + act_type = bundle.get_service_name() + home_model = self._model.get_home() + home_model.notify_activity_launch(activity_id, act_type) + + handler = ActivityFactory.create(act_type) + handler.connect('success', self._join_success_cb, activity_ps, activity_id, act_type) + handler.connect('error', self._join_error_cb, home_model, activity_id, act_type) + + def _find_unique_activity_id(self): + # create a new unique activity ID + i = 0 + act_id = None + while i < 10: + act_id = sugar.util.unique_id() + i += 1 + + # check through existing activities + found = False + for xid, act_host in self._hosts.items(): + if act_host.get_id() == act_id: + found = True + break + if found: + act_id = None + continue + + # check through network activities + activities = self._pservice.get_activities() + for act in activities: + if act_id == act.get_id(): + found = True + break + if found: + act_id = None + continue + + return act_id + + def _start_success_cb(self, handler, activity, activity_id, activity_type): + logging.debug("Started activity %s (%s)" % (activity_id, activity_type)) + activity.start(activity_id) + + def _start_error_cb(self, handler, err, home_model, activity_id, activity_type): + logging.error("Couldn't launch activity %s (%s):\n%s" % (activity_id, activity_type, err)) + home_mode.notify_activity_launch_failed(activity_id) def start_activity(self, activity_type): logging.debug('Shell.start_activity') - activity = ActivityFactory.create(activity_type) - activity.start() - return activity + act_id = self._find_unique_activity_id() + if not act_id: + logging.error("Couldn't find available activity ID.") + return None + + home_model = self._model.get_home() + home_model.notify_activity_launch(act_id, activity_type) + + logging.debug("Shell.start_activity will start %s (%s)" % (act_id, activity_type)) + handler = ActivityFactory.create(activity_type) + handler.connect('success', self._start_success_cb, act_id, activity_type) + handler.connect('error', self._start_error_cb, home_model, act_id, activity_type) def set_zoom_level(self, level): if level == sugar.ZOOM_ACTIVITY: diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index ade37bd..42c5453 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -4,6 +4,7 @@ from sugar.graphics.menuicon import MenuIcon from view.clipboardmenu import ClipboardMenu from sugar.activity import ActivityFactory from sugar.clipboard import clipboardservice +from sugar import util class ClipboardIcon(MenuIcon): @@ -39,22 +40,32 @@ class ClipboardIcon(MenuIcon): else: return None + def _activity_create_success_cb(self, handler, activity): + activity.start(util.unique_id()) + activity.execute("open_document", [self._object_id]) + + def _activity_create_error_cb(self, handler, err): + pass + def _icon_activated_cb(self, icon): - if self._percent == 100: - cb_service = clipboardservice.get_instance() - - (name, percent, icon, preview, format_types) = \ - cb_service.get_object(self._object_id) + if self._percent < 100: + return - if format_types: - logging.debug("_icon_activated_cb: " + self._object_id) - - activity_id = self._get_activity_for_mime_type(format_types[0]) - - if activity_id: - activity = ActivityFactory.create(activity_id) - activity.start() - activity.execute("open_document", [self._object_id]) + cb_service = clipboardservice.get_instance() + (name, percent, icon, preview, format_types) = \ + cb_service.get_object(self._object_id) + if not format_types: + return + + logging.debug("_icon_activated_cb: " + self._object_id) + activity_type = self._get_activity_for_mime_type(format_types[0]) + if not activity_type: + return + + # Launch the activity to handle this item + handler = ActivityFactory.create(activity_type) + handler.connect('success', self._activity_create_success_cb) + handler.connect('error', self._activity_create_error_cb) def _popup_action_cb(self, popup, action): self.popdown() diff --git a/shell/view/home/activitiesdonut.py b/shell/view/home/activitiesdonut.py index b4fe22a..5e4d4f2 100644 --- a/shell/view/home/activitiesdonut.py +++ b/shell/view/home/activitiesdonut.py @@ -16,10 +16,44 @@ import hippo import math +import gobject from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics import style +class ActivityIcon(CanvasIcon): + def __init__(self, activity): + icon_name = activity.get_icon_name() + icon_color = activity.get_icon_color() + CanvasIcon.__init__(self, icon_name=icon_name, color=icon_color) + style.apply_stylesheet(self, 'ring.ActivityIcon') + + self._activity = activity + self._pulse_id = 0 + self._launched = False + + self._pulse_id = gobject.timeout_add(200, self._pulse_cb) + + def __del__(self): + if self._pulse_id > 0: + gobject.source_remove(self._pulse_id) + + def _pulse_cb(self): + pass + + def set_launched(self): + if self._launched: + return + self._launched = True + gobject.source_remove(self._pulse_id) + self._pulse_id = 0 + + def get_launched(self): + return self._launched + + def get_activity(self): + return self._activity + class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem): __gtype_name__ = 'SugarActivitiesDonut' def __init__(self, shell, **kwargs): @@ -29,34 +63,46 @@ class ActivitiesDonut(hippo.CanvasBox, hippo.CanvasItem): self._shell = shell self._model = shell.get_model().get_home() + self._model.connect('activity-launched', self._activity_launched_cb) self._model.connect('activity-added', self._activity_added_cb) - self._model.connect('activity-removed', self._activity_removed_cb) + self._model.connect('activity-removed', self._activity_removed_cb) - def _activity_added_cb(self, model, activity): + def _activity_launched_cb(self, model, activity): self._add_activity(activity) + def _activity_added_cb(self, model, activity): + # Mark the activity as launched + act_id = activity.get_id() + if not self._activities.has_key(act_id): + return + icon = self._activities[act_id] + icon.set_launched() + def _activity_removed_cb(self, model, activity): self._remove_activity(activity) def _remove_activity(self, activity): - icon = self._activities[activity.get_id()] + act_id = activity.get_id() + if not self._activities.has_key(act_id): + return + icon = self._activities[act_id] self.remove(icon) - del self._activities[activity.get_id()] + del self._activities[act_id] def _add_activity(self, activity): - icon_name = activity.get_icon_name() - icon_color = activity.get_icon_color() - - icon = CanvasIcon(icon_name=icon_name, color=icon_color) - style.apply_stylesheet(icon, 'ring.ActivityIcon') - icon.connect('activated', self._activity_icon_clicked_cb, activity) + icon = ActivityIcon(activity) + icon.connect('activated', self._activity_icon_clicked_cb) self.append(icon, hippo.PACK_FIXED) self._activities[activity.get_id()] = icon self.emit_paint_needed(0, 0, -1, -1) - def _activity_icon_clicked_cb(self, item, activity): + def _activity_icon_clicked_cb(self, icon): + activity = icon.get_activity() + if not icon.get_launched(): + return + activity_host = self._shell.get_activity(activity.get_id()) if activity_host: activity_host.present() -- cgit v0.9.1