import sys sys.path.append("..") import dbus from gettext import gettext as _ import gobject import gtk from plugin import Plugin from util.menubuilder import MenuBuilder from util.configfile import ConfigFile from util.configwizard import ConfigWizard import telepathy from collaboration.neighborhood import get_neighborhood from collaboration.connectionmanager import get_connection_manager from collaboration.activity import Activity from collaboration import telepathyclient from collaboration.tubeconn import TubeConnection import traceback from TurtleArt.tacollaboration import Collaboration CONNECTION_INTERFACE_ACTIVITY_PROPERTIES = \ 'org.laptop.Telepathy.ActivityProperties' class CollaborationPlugin(Plugin): __gsignals__ = { 'joined': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), 'shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ()), } # Using activity here is kinda misleading cause the # obj we are receiving is not a Sugar activity. def __init__(self, activity, config_file_path): Plugin.__init__(self) self._activity = activity self._neighborhood = None self._title = "My Turtle Art session" self._bundle_id = "org.laptop.TurtleArt" self._activity_id = "1234567" # This could be hashed from the file path (if resuming) self._nick = "" self._config_file_path = config_file_path self._collaboration_config_values = ConfigFile(self._config_file_path) valid_config_values = { "nick" : { "type" : "text"}, "account_id" : { "type" : "text"}, "password" : { "type" : "text"}, "server" : { "type" : "text"}, "port" : { "type" : "integer"}, "register": { "type" : "boolean"}, "turtle_color" : { "type" : "text"}, "colors" : { "type" : "text"} } self._collaboration_config_values.set_valid_keys(valid_config_values) self._collaboration_config_values.connect("configuration-loaded", self._connect_to_neighborhood) self._collaboration_config_values.connect("configuration-saved", self._connect_to_neighborhood) self._collaboration_config_values.load() def setup(self): self._collaboration = Collaboration(self.tw, self) self._collaboration.setup() def set_tw(self, turtleart_window): self.tw = turtleart_window self.tw.nick = self._get_nick() def get_menu(self): menu = gtk.Menu() self._activities_submenu = gtk.Menu() activities_menu = MenuBuilder.make_sub_menu(self._activities_submenu, _('Activities')) menu.append(activities_menu) self._buddies_submenu = gtk.Menu() buddies_menu = MenuBuilder.make_sub_menu(self._buddies_submenu, _('Buddies')) menu.append(buddies_menu) MenuBuilder.make_menu_item(menu, _('Share'), self._share_cb) MenuBuilder.make_menu_item(menu, _('Configuration'), self._config_neighborhood_cb) neighborhood_menu = MenuBuilder.make_sub_menu(menu, _('Neighborhood')) return neighborhood_menu def get_colors(self): return self._colors def _get_nick(self): return self._nick def _get_activity_id(self): return self._activity_id def _get_bundle_id(self): return self._bundle_id def _get_title(self): return self._title def _get_turtle_color(self): return self._turtle_color def _connect_to_neighborhood(self, config_file_obj): if self._neighborhood is not None: return print "_connect_to_neighborhood has been called" params = {} params["nickname"] = self._collaboration_config_values.get("nick") params["account_id"] = self._collaboration_config_values.get("account_id") params["server"] = self._collaboration_config_values.get("server") params["port"] = self._collaboration_config_values.get("port") params["password"] = self._collaboration_config_values.get("password") params["register"] = self._collaboration_config_values.get("register") if params["server"] == "": raise RuntimeError("Invalid server address") self._nick = self._collaboration_config_values.get("nick") self._colors = self._collaboration_config_values.get("colors") self._turtle_color = self._collaboration_config_values.get("turtle_color") self._activities = {} self._buddies = {} print "connecting to the neighborhood" dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) self._client_handler = telepathyclient.get_instance() self._neighborhood = get_neighborhood(params) self._neighborhood.connect('activity-added', self._activity_added_cb) self._neighborhood.connect('activity-removed', self._activity_removed_cb) self._neighborhood.connect('buddy-added', self._buddy_added_cb) self._neighborhood.connect('buddy-removed', self._buddy_removed_cb) # TODO: # - show nick of sharer # - show icon with color of sharer def _activity_added_cb(self, model, activity_model): self._activities[activity_model.props.name] = activity_model self._recreate_available_activities_menu() def _activity_removed_cb(self, model, activity_model): try: self._activities.pop(activity_model.props.name) except: print "Failed to remove activity %s" % activity_model.props.name self._recreate_available_activities_menu() def _buddy_added_cb(self, activity, buddy): self._buddies[buddy.get_key()] = buddy self._recreate_available_buddies_menu() def _buddy_removed_cb(self, activity, buddy): try: self._buddies.pop(buddy.get_key()) except: print "Couldn't remove buddy %s" % buddy.get_key() self._recreate_available_buddies_menu() # TODO: we should have a list of available actions over # a given buddy. I.e.: a) chat with him b) make friend # c) invite to current activity # def _recreate_available_buddies_menu(self): for child in self._buddies_submenu.get_children(): self._buddies_submenu.remove(child) for buddy in self._buddies.values(): key = buddy.get_key() if key is None: key = "" n = buddy.get_nick() + "|" + key[0:15] MenuBuilder.make_menu_item(self._buddies_submenu, n, self._buddy_actions_cb, buddy) def _buddy_actions_cb(self, widget, buddy): print "do something with %s" % buddy.get_nick() # TODO # - we need an extra menu branch with a) 'Join' button b) List of buddies def _recreate_available_activities_menu(self): for child in self._activities_submenu.get_children(): self._activities_submenu.remove(child) for activity in self._activities.values(): n = activity.props.name MenuBuilder.make_menu_item(self._activities_submenu, n, self._join_activity_cb, activity) def _join_activity_cb(self, widget, activity): print "Lets try to join..." connection_manager = get_connection_manager() account_path, connection = \ connection_manager.get_preferred_connection() if connection is None: print('No active connection available') return properties = {} properties["id"] = activity.activity_id properties["color"] = activity.get_color() print "room handle according to activity %s" % activity.room_handle properties["private"] = True try: room_handle = connection.GetActivity(activity.activity_id, dbus_interface=CONNECTION_INTERFACE_ACTIVITY_PROPERTIES) print("room_handle = %s" % str(room_handle)) self._joined_activity = Activity(account_path, connection, room_handle, properties=properties) # FIXME: this should be unified, no need to keep 2 references self._shared_activity = self._joined_activity except: traceback.print_exc(file=sys.stdout) if self._joined_activity.props.joined: raise RuntimeError('Activity %s is already shared.' % activity.activity_id) _join_id = self._joined_activity.connect('joined', self.__joined_cb) self._joined_activity.join() def __joined_cb(self,activity, success, err): print "We've joined an activity" self.emit('joined') def _config_neighborhood_cb(self, widget): config_w = ConfigWizard(self._config_file_path) config_items = [ {"item_label" : _("Nickname"), "item_type" : "text", "item_name" : "nick" }, { "item_label" : _("Account ID"), "item_type" : "text", "item_name" : "account_id" }, { "item_label" : _("Server"), "item_type" : "text", "item_name" : "server" }, { "item_label" : _("Port"), "item_type" : "text", "item_name" : "port" }, { "item_label" : _("Password"), "item_type" : "text", "item_name" : "password" }, { "item_label" : _("Register"), "item_type" : "boolean", "item_name" : "register" }, { "item_label" : _("Colors"), "item_type" : "text", "item_name" : "colors" }, { "item_label" : _("Turtle Color"), "item_type" : "text", "item_name" : "turtle_color" } ] config_w.set_config_items(config_items) config_w.set_config_file_obj(self._collaboration_config_values) config_w.show() def _share_cb(self, button): properties = {} properties['id'] = self._get_activity_id() properties['type'] = self._get_bundle_id() properties['name'] = self._get_title() properties['color'] = self._get_turtle_color() properties['private'] = False connection_manager = get_connection_manager() account_path, connection = \ connection_manager.get_preferred_connection() if connection is None: print('No active connection available') return try: self._activity._shared_activity = Activity(account_path, connection, properties=properties) # FIXME: this should be unified, no need to keep 2 references self._shared_activity = self._activity._shared_activity except: traceback.print_exc(file=sys.stdout) if self._activity._shared_activity.props.joined: raise RuntimeError('Activity %s is already shared.' % self._activity._get_activity_id()) self._activity._shared_activity.share(self.__share_activity_cb, self.__share_activity_error_cb) def __share_activity_cb(self, activity): """Finish sharing the activity""" self.emit('shared') def __share_activity_error_cb(self, activity, error): """Notify with GObject event of unsuccessful sharing of activity """ print "%s got error: %s" % (activity, error) if __name__ == "__main__": print "testing collaboration"