Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--extra/collaborationplugin.py280
-rw-r--r--extra/plugin.py10
-rwxr-xr-xturtleart.py18
3 files changed, 306 insertions, 2 deletions
diff --git a/extra/collaborationplugin.py b/extra/collaborationplugin.py
new file mode 100644
index 0000000..145e3de
--- /dev/null
+++ b/extra/collaborationplugin.py
@@ -0,0 +1,280 @@
+
+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
+from TurtleArt.tacollaboration import Collaboration
+
+
+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
+
+ 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)
+ 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):
+ 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._activity._get_activity_id()
+ properties['type'] = self._activity._get_bundle_id()
+ properties['name'] = self._activity._get_title()
+ properties['color'] = self._activity._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)
+ 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"
+
diff --git a/extra/plugin.py b/extra/plugin.py
new file mode 100644
index 0000000..fe95f95
--- /dev/null
+++ b/extra/plugin.py
@@ -0,0 +1,10 @@
+
+import gobject
+
+class Plugin(gobject.GObject):
+ def __init__(self):
+ gobject.GObject.__init__(self)
+
+ def get_menu(self):
+ raise RuntimeError("You need to define get_menu for your plugin.")
+
diff --git a/turtleart.py b/turtleart.py
index c32677b..b8ffa08 100755
--- a/turtleart.py
+++ b/turtleart.py
@@ -50,6 +50,7 @@ from TurtleArt.tawindow import TurtleArtWindow
from TurtleArt.taexporthtml import save_html
from TurtleArt.taexportlogo import save_logo
from extra.upload import Uploader
+from extra.collaborationplugin import CollaborationPlugin
from util.menubuilder import MenuBuilder
class TurtleMain():
@@ -78,11 +79,22 @@ class TurtleMain():
self._draw_and_quit()
else:
self._read_initial_pos()
+ self._init_plugins()
self._setup_gtk()
self._build_window()
- self._uploader.set_tw(self.tw)
+ self._run_plugins()
self._start_gtk()
+ def _init_plugins(self):
+ config_file_path = os.path.join(CONFIG_HOME, 'turtleartrc.collab')
+ self._collab_plugin = CollaborationPlugin(self, config_file_path)
+ self._uploader = Uploader()
+
+ def _run_plugins(self):
+ self._uploader.set_tw(self.tw)
+ self._collab_plugin.set_tw(self.tw)
+ self._collab_plugin.setup()
+
def _mkdir_p(path):
'''Create a directory in a fashion similar to `mkdir -p`'''
try:
@@ -135,7 +147,6 @@ class TurtleMain():
# sure our current directory is TA's source dir.
os.chdir(os.path.dirname(__file__))
- self._uploader = Uploader()
self.ta_file = None
self.output_png = False
self.i = 0 # FIXME: use a better name for this variable
@@ -292,12 +303,15 @@ class TurtleMain():
MenuBuilder.make_menu_item(menu, _('Stop'), self._do_stop_cb)
turtle_menu = MenuBuilder.make_sub_menu(menu, _('Turtle'))
+ collaboration_menu = self._collab_plugin.get_menu()
+
menu_bar = gtk.MenuBar()
menu_bar.append(activity_menu)
menu_bar.append(edit_menu)
menu_bar.append(view_menu)
menu_bar.append(tool_menu)
menu_bar.append(turtle_menu)
+ menu_bar.append(collaboration_menu)
return menu_bar
def _quit_ta(self, widget=None, e=None):