diff --git a/shell/model/homeactivity.py b/shell/model/homeactivity.py index 2143ef2e..efaa2bb1 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 f4fd3eec..f7d16c9a 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(window.get_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/view/Shell.py b/shell/view/Shell.py index 169ee353..9e81cab4 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -205,13 +205,16 @@ class Shell(gobject.GObject): breg = self._model.get_bundle_registry() bundle = breg.find_by_default_type(bundle_id) if bundle: - serv_name = bundle.get_service_name() + act_type = bundle.get_service_name() + home_model = self._model.get_home() + home_model.notify_activity_launch(activity_id, act_type) try: - activity = ActivityFactory.create(serv_name) + activity = ActivityFactory.create(act_type) except DBusException, e: - logging.error("Couldn't launch activity %s:\n%s" % (serv_name, e)) + logging.error("Couldn't launch activity %s:\n%s" % (act_type, e)) + home_mode.notify_activity_launch_failed(activity_id) else: - logging.debug("Joining activity type %s id %s" % (serv_name, activity_id)) + logging.debug("Joining activity type %s id %s" % (act_type, activity_id)) activity.join(activity_ps.object_path()) else: logging.error("Couldn't find activity for type %s" % bundle_id) @@ -255,11 +258,15 @@ class Shell(gobject.GObject): 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) + try: logging.debug("Shell.start_activity will start %s:%s" % (activity_type, act_id)) activity = ActivityFactory.create(activity_type) except dbus.DBusException, e: logging.debug("Couldn't start activity '%s':\n %s" % (activity_type, e)) + home_mode.notify_activity_launch_failed(act_id) return None activity.start(act_id)