From a1e4d136f860b03abcafc7bf2e2d65b412bc13cd Mon Sep 17 00:00:00 2001 From: JCTutorius Date: Sun, 27 Dec 2009 19:32:14 +0000 Subject: Merge branch 'master' of gitorious@git.sugarlabs.org:tutorius/mainline --- (limited to 'tutorius/TProbe.py') diff --git a/tutorius/TProbe.py b/tutorius/TProbe.py index 5508d49..be0270a 100644 --- a/tutorius/TProbe.py +++ b/tutorius/TProbe.py @@ -14,6 +14,7 @@ # 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 logging LOGGER = logging.getLogger("sugar.tutorius.TProbe") import os @@ -26,7 +27,10 @@ import cPickle as pickle from functools import partial +import gtk from jarabe.model.shell import get_model +from jarabe.model import bundleregistry +from sugar.activity import activityfactory from sugar.bundle.activitybundle import ActivityBundle from . import addon @@ -156,11 +160,13 @@ class TProbe(dbus.service.Object): if not is_editing: action.do(activity=self._activity, probe=self, overlayer=self._overlayer) else: + action.set_notification_cb(partial(self.update_action, address)) + # force mandatory props addon_name = addon.get_name_from_type(type(action)) meta = addon.get_addon_meta(addon_name) for propname in meta['mandatory_props']: - if getattr(action, propname) != None: + if getattr(action, propname, False): continue prop = getattr(type(action), propname) prop.widget_class.run_dialog(self._activity, @@ -169,7 +175,6 @@ class TProbe(dbus.service.Object): updated_props[propname] = getattr(action, propname) action.enter_editmode(overlayer=self._overlayer) - action.set_notification_cb(partial(self.update_action, address)) pickled_value = pickle.dumps((address, updated_props)) return pickled_value @@ -498,6 +503,107 @@ class DesktopProbe(TProbe): return "desktop://"+window+"/"+(".".join(name)) + # ------------------ Helper functions specific to a component -------------- + def find_widget(self, base, path, ignore_errors=True): + """ + Finds a widget from a base object. Symmetric with retrieve_path + + @param base the parent widget + @param path fqdn-style target object name + + @return widget found + """ + return find_widget(base, path, ignore_errors) + + def retrieve_path(self, widget): + """ + Retrieve the path to access a specific widget. + Symmetric with find_widget. + + @param widget the widget to find a path for + + @return path to the widget + """ + return raddr_lookup(widget) + +class FrameProbe(TProbe): + """ + Identical to the base probe except that helper functions are redefined + to handle the four windows that are part of the Frame. + """ + # ------------------ Helper functions specific to a component -------------- + def find_widget(self, base, path, ignore_errors=True): + """ + Finds a widget from a base object. Symmetric with retrieve_path + + format for the path for the frame should be: + + frame:/// + where panel: top | bottom | left | right + path: number[.number]* + + @param base the parent widget + @param path fqdn-style target object name + + @return widget found + """ + protocol, p = path.split("://") + assert protocol == "frame" + + window, object_id = p.split("/") + if window == "top": + return find_widget(base._top_panel, object_id, ignore_errors) + elif window == "bottom": + return find_widget(base._bottom_panel, object_id, ignore_errors) + elif window == "left": + return find_widget(base._left_panel, object_id, ignore_errors) + elif window == "right": + return find_widget(base._right_panel, object_id, ignore_errors) + else: + raise RuntimeWarning("Invalid frame panel: '%s'"%window) + + return find_widget(base, path, ignore_errors) + + def retrieve_path(self, widget): + """ + Retrieve the path to access a specific widget. + Symmetric with find_widget. + + format for the path for the frame should be: + + frame:/// + where panel: top | bottom | left | right + path: number[.number]* + + @param widget the widget to find a path for + + @return path to the widget + """ + name = [] + child = widget + parent = widget.parent + while parent: + name.append(str(parent.get_children().index(child))) + child = parent + parent = child.parent + + name.append("0") # root object itself + name.reverse() + + window = "" + if parent._position == gtk.POS_TOP: + window = "top" + elif parent._position == gtk.POS_BOTTOM: + window = "bottom" + elif parent._position == gtk.POS_LEFT: + window = "left" + elif parent._position == gtk.POS_RIGHT: + window = "right" + else: + raise RuntimeWarning("Invalid root panel in frame: %s"%str(parent)) + + return "frame://"+window+"/"+(".".join(name)) + class ProbeProxy: """ ProbeProxy is a Proxy class for connecting to a remote TProbe. @@ -673,15 +779,24 @@ class ProbeProxy: else: LOGGER.debug("ProbeProxy :: unsubsribe address %s inconsistency : not registered", address) - def create_event(self, addon_name): + def create_event(self, addon_name, event_created_cb): """ Create an event on the app side and request the user to fill the properties before returning it. @param addon_name: the add-on name of the event + @param event_created_cb The notification to trigger once the event has + been instantiated @returns: an eventfilter instance """ - return pickle.loads(str(self._probe.create_event(addon_name))) + self._probe.create_event(addon_name, + reply_handler=save_args(self._event_created_cb, event_created_cb), + error_handler=ignore) + + def _event_created_cb(self, event_created_cb, event): + LOGGER.debug("ProbeProxy :: _event_created_cb, calling upper layer") + event = pickle.loads(str(event)) + event_created_cb(event) def subscribe(self, event, notification_cb, event_subscribed_cb, error_cb): @@ -751,20 +866,33 @@ class ProbeManager(object): self._probes = {} self._current_activity = None + self.list_pending_actions = [] + self.list_action_installed_cb = [] + self.list_error_cb = [] + + self.list_pending_transitions = [] + self.list_notification_cb = [] + self.list_event_subscribed_cb = [] + self.list_error_cb = [] + + self.is_activity_launching = False + ProbeManager._LOGGER.debug("__init__()") def setCurrentActivity(self, activity_id): - if not activity_id in self._probes: - raise RuntimeError("Activity not attached, id : %s"%activity_id) + # HACK : Disabling check for now, since it prevents usage of probes + # in activities that have yet to register their probes... We might + # set the current activity before having to execute anything inside it + # e.g. A new source is crawling in and we need to start the activity + # + # This should be removed once the Home Window probes are installed. + + #if not activity_id in self._probes: + # raise RuntimeError("Activity not attached, id : %s"%activity_id) + LOGGER.debug("ProbeManager :: New activity set as current = %s", str(activity_id)) self._current_activity = activity_id def getCurrentActivity(self): - # TODO : Insert the correct call to remember the current activity, - # taking the views and frame into account - current_act = get_model().get_active_activity() - current_act_bundle = ActivityBundle(current_act.get_bundle_path()) - current_act_id = current_act_bundle.get_bundle_id() - self._current_activity = current_act_id return self._current_activity currentActivity = property(fget=getCurrentActivity, fset=setCurrentActivity) @@ -775,6 +903,43 @@ class ProbeManager(object): else: return None + def prelaunch_activity(self, activity, action_event, is_event=False): + if activity == "org.sugar.desktop.mesh": + get_model()._set_zoom_level(get_model().ZOOM_MESH) + return False + elif activity == "org.sugar.desktop.group": + get_model()._set_zoom_level(get_model().ZOOM_GROUP) + return False + elif activity == "org.sugar.desktop.home": + get_model()._set_zoom_level(get_model().ZOOM_HOME) + return False + + if activity == get_model().get_active_activity().get_type(): + return False + + model = get_model() + for active_activity in model: + if active_activity is not None and active_activity.get_type() == activity: + active_activity.get_window().activate(gtk.get_current_event_time()) + return False + + bundle = bundleregistry.get_registry().get_bundle(activity) + if not bundle: + print 'WARNING : Cannot find bundle' + else: + path = bundle.get_path() + activity_bundle = ActivityBundle(path) + if self.is_activity_launching == False: + activityfactory.create(activity_bundle) + self.is_activity_launching = True + + if is_event: + self.list_pending_transitions.append(action_event) + else: + self.list_pending_actions.append(action_event) + return True + return False + def install(self, action, action_installed_cb, error_cb, is_editing=False, editing_cb=None): """ Install an action on the current activity @@ -792,6 +957,13 @@ class ProbeManager(object): activity = self.currentActivity if activity: + wait_install = self.prelaunch_activity(activity, action) + + if wait_install: + self.list_action_installed_cb.append(action_installed_cb) + self.list_error_cb.append(error_cb) + return + return self._first_proxy(activity).install( action=action, is_editing=is_editing, @@ -840,16 +1012,17 @@ class ProbeManager(object): else: raise RuntimeWarning("No activity attached") - def create_event(self, addon_name): + def create_event(self, addon_name, event_created_cb): """ Create an event on the app side and request the user to fill the properties before returning it. @param addon_name: the add-on name of the event + @param event_created_cb The notification to send once the event was created @returns: an eventfilter instance """ if self.currentActivity: - return self._first_proxy(self.currentActivity).create_event(addon_name) + return self._first_proxy(self.currentActivity).create_event(addon_name, event_created_cb) else: raise RuntimeWarning("No activity attached") @@ -867,6 +1040,14 @@ class ProbeManager(object): activity = self.get_source_activity(event) if activity: + wait_install = self.prelaunch_activity(activity, event, True) + + if wait_install: + self.list_notification_cb.append(notification_cb) + self.list_event_subscribed_cb.append(event_subscribed_cb) + self.list_error_cb.append(error_cb) + return + return self._first_proxy(activity).subscribe(event, notification_cb,\ event_subscribed_cb, error_cb) else: @@ -906,11 +1087,47 @@ class ProbeManager(object): process_name = str(process_name) unique_id = str(unique_id) ProbeManager._LOGGER.debug("register_probe(%s,%s)", process_name, unique_id) + if process_name not in self._probes: self._probes[process_name] = [(unique_id,self._ProxyClass(process_name, unique_id))] else: self._probes[process_name].append((unique_id,self._ProxyClass(process_name, unique_id))) + # Register the probe that was just installed as the current activity + # (this will be true by default since we probably were waiting for it + # to open up) + self.currentActivity = process_name + cnt_action = 0 + for pending_action in self.list_pending_actions: + self._first_proxy(self.currentActivity).install( + action=pending_action, + is_editing=False, + action_installed_cb=self.list_action_installed_cb[cnt_action], + error_cb=self.list_error_cb[cnt_action], + editing_cb=False + ) + cnt_action = cnt_action + 1 + + cnt_transition = 0 + for pending_transition in self.list_pending_transitions: + self._first_proxy(self.currentActivity).subscribe( + pending_transition, + self.list_notification_cb[cnt_transition], + self.list_event_subscribed_cb[cnt_transition], + self.list_error_cb[cnt_transition] + ) + cnt_transition = cnt_transition + 1 + + self.list_pending_actions = [] + self.list_action_installed_cb = [] + self.list_error_cb = [] + + self.list_pending_transitions = [] + self.list_notification_cb = [] + self.list_event_subscribed_cb = [] + self.list_error_cb = [] + + self.is_activity_launching = False def unregister_probe(self, unique_id): """ Remove a probe from the known probes. -- cgit v0.9.1