diff options
Diffstat (limited to 'ShareFavorites.py')
-rw-r--r-- | ShareFavorites.py | 294 |
1 files changed, 294 insertions, 0 deletions
diff --git a/ShareFavorites.py b/ShareFavorites.py new file mode 100644 index 0000000..10d65c8 --- /dev/null +++ b/ShareFavorites.py @@ -0,0 +1,294 @@ +# -*- coding: utf-8 -*- +#Copyright (c) 2013 Walter Bender + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# You should have received a copy of the GNU General Public License +# along with this library; if not, write to the Free Software +# Foundation, 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA + + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GdkPixbuf +from gi.repository import GObject + +import subprocess +import os + +from sugar3.activity import activity +from sugar3 import profile +from sugar3 import env + +from sugar3.graphics.toolbarbox import ToolbarBox +from sugar3.activity.widgets import ActivityToolbarButton +from sugar3.activity.widgets import StopButton +from sugar3.graphics.alert import Alert + +from toolbar_utils import (button_factory, separator_factory) +from gettext import gettext as _ + +import logging +_logger = logging.getLogger("share-favorites") + +import json + +import telepathy +from dbus.service import signal +from dbus.gobject_service import ExportedGObject +from sugar3.presence import presenceservice +from sugar3.presence.tubeconn import TubeConnection + + +SERVICE = 'org.sugarlabs.ShareFavorites' +IFACE = SERVICE +PATH = '/org/sugarlabs/ShareFavorites' + + +class ShareFavorites(activity.Activity): + ''' Share desktop favorites ''' + + def __init__(self, handle): + ''' Initialize the toolbars and the work surface ''' + super(ShareFavorites, self).__init__(handle) + + self._buddies = [profile.get_nick_name()] + self._colors = profile.get_color().to_string().split(',') + self._my_colors = self._colors[:] # Save original colors + self.initiating = None # sharing (True) or joining (False) + + self._first_time = True + + self.old_cursor = self.get_window().get_cursor() + + self._setup_toolbars() + self._setup_canvas() + + self._setup_presence_service() + + def _setup_canvas(self): + ''' Create a canvas ''' + self.fixed = Gtk.Fixed() + self.fixed.show() + self.set_canvas(self.fixed) + + self.vbox = Gtk.VBox(False, 0) + self.vbox.set_size_request(Gdk.Screen.width(), Gdk.Screen.height()) + self.fixed.put(self.vbox, 0, 0) + self.vbox.show() + + self._canvas = Gtk.DrawingArea() + self._canvas.set_size_request(int(Gdk.Screen.width()), + int(Gdk.Screen.height())) + self._canvas.show() + self.show_all() + self.vbox.pack_end(self._canvas, True, True, 0) + self.vbox.show() + + def _setup_toolbars(self): + ''' Setup the toolbars. ''' + self.max_participants = 25 # sharing + + toolbox = ToolbarBox() + + # Activity toolbar + activity_button_toolbar = ActivityToolbarButton(self) + + toolbox.toolbar.insert(activity_button_toolbar, 0) + activity_button_toolbar.show() + + self.set_toolbar_box(toolbox) + toolbox.show() + self.toolbar = toolbox.toolbar + + separator_factory(toolbox.toolbar, True, False) + + stop_button = StopButton(self) + stop_button.props.accelerator = '<Ctrl>q' + toolbox.toolbar.insert(stop_button, -1) + stop_button.show() + + toolbox.toolbar.show_all() + + def _restore_cursor(self): + ''' No longer waiting, so restore standard cursor. ''' + if not hasattr(self, 'get_window'): + return + self.get_window().set_cursor(self.old_cursor) + + def _waiting_cursor(self): + ''' Waiting, so set watch cursor. ''' + if not hasattr(self, 'get_window'): + return + self.old_cursor = self.get_window().get_cursor() + self.get_window().set_cursor(Gdk.Cursor.new(Gdk.CursorType.WATCH)) + + def _dump(self, favorites): + ''' Dump favorites for sharing. ''' + _logger.debug('dumping %s' % (json.dumps(favorites))) + return json.dumps(favorites) + + def _load(self, data): + ''' Load favorites from a sharer. ''' + favorites = json.loads(data) + + # When favorites are shared, the sharer sends out list; joiners + # receive the list. + def _read_favorites(self): + favorites_path = os.path.join(env.get_profile_path(), + 'favorite_activities') + logging.debug('read favorities: %s' % (favorites_path)) + fd = open(favorites_path, 'r') + data = fd.read() + fd.close() + return self._dump(data) + + def _save_favorites(self, favorites): + favorites_path = os.path.join(env.get_profile_path(), + 'favorite_activities') + logging.debug('save favorities: %s' % (favorites_path)) + fd = open(favorites_path, 'w') + fd.write(favorites) + fd.close() + + def _setup_presence_service(self): + ''' Setup the Presence Service. ''' + self.pservice = presenceservice.get_instance() + + owner = self.pservice.get_owner() + self.owner = owner + self.buddies = [owner] + self._share = '' + self.connect('shared', self._shared_cb) + self.connect('joined', self._joined_cb) + + def _shared_cb(self, activity): + ''' Either set up initial share...''' + if self.get_shared_activity() is None: + _logger.error('Failed to share or join activity ... \ + shared_activity is null in _shared_cb()') + return + + self.initiating = True + self.waiting = False + _logger.debug('I am sharing...') + + self.conn = self.shared_activity.telepathy_conn + self.tubes_chan = self.shared_activity.telepathy_tubes_chan + self.text_chan = self.shared_activity.telepathy_text_chan + + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal( + 'NewTube', self._new_tube_cb) + + _logger.debug('This is my activity: making a tube...') + id = self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].OfferDBusTube( + SERVICE, {}) + + self._share_favorites() # delete me + + def _joined_cb(self, activity): + ''' ...or join an exisiting share. ''' + if self.get_shared_activity() is None: + _logger.error('Failed to share or join activity ... \ + shared_activity is null in _shared_cb()') + return + + self.initiating = False + _logger.debug('I joined a shared activity.') + + self.conn = self.shared_activity.telepathy_conn + self.tubes_chan = self.shared_activity.telepathy_tubes_chan + self.text_chan = self.shared_activity.telepathy_text_chan + + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].connect_to_signal(\ + 'NewTube', self._new_tube_cb) + + _logger.debug('I am joining an activity: waiting for a tube...') + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES].ListTubes( + reply_handler=self._list_tubes_reply_cb, + error_handler=self._list_tubes_error_cb) + + self.waiting = True + self._waiting_cursor() + + def _list_tubes_reply_cb(self, tubes): + ''' Reply to a list request. ''' + for tube_info in tubes: + self._new_tube_cb(*tube_info) + + def _list_tubes_error_cb(self, e): + ''' Log errors. ''' + _logger.error('ListTubes() failed: %s', e) + + def _new_tube_cb(self, id, initiator, type, service, params, state): + ''' Create a new tube. ''' + _logger.debug('New tube: ID=%d initator=%d type=%d service=%s ' + 'params=%r state=%d', id, initiator, type, service, + params, state) + + if (type == telepathy.TUBE_TYPE_DBUS and service == SERVICE): + if state == telepathy.TUBE_STATE_LOCAL_PENDING: + self.tubes_chan[ \ + telepathy.CHANNEL_TYPE_TUBES].AcceptDBusTube(id) + + tube_conn = TubeConnection(self.conn, + self.tubes_chan[telepathy.CHANNEL_TYPE_TUBES], id, \ + group_iface=self.text_chan[telepathy.CHANNEL_INTERFACE_GROUP]) + + self.chattube = ChatTube(tube_conn, self.initiating, \ + self.event_received_cb) + + def event_received_cb(self, text): + ''' Data is passed as tuples: cmd:text ''' + dispatch_table = {'F': self._update_favorites, + 'f': self._share_favorites, + } + _logger.debug('<<< %s' % (text[0])) + dispatch_table[text[0]](text[2:]) + + def _new_join(self, data): + if self.initiating: + self._share_favorites() + + def _update_favorites(self, data): + favorites = self._dump(data) + self._save_favorites(favorities) + self._restore_cursor() + # TODO: Update homeview + + def _share_favorites(self): + if self.initiating: + _logger.debug('sharing favorites') + self._send_event('F:%s' % (self._read_favorites())) + + def _send_event(self, text): + ''' Send event through the tube. ''' + if hasattr(self, 'chattube') and self.chattube is not None: + _logger.debug('>>> %s' % (text[0])) + self.chattube.SendText(text) + + +class ChatTube(ExportedGObject): + ''' Class for setting up tube for sharing ''' + def __init__(self, tube, is_initiator, stack_received_cb): + super(ChatTube, self).__init__(tube, PATH) + self.tube = tube + self.is_initiator = is_initiator # Are we sharing or joining activity? + self.stack_received_cb = stack_received_cb + self.stack = '' + + self.tube.add_signal_receiver(self.send_stack_cb, 'SendText', IFACE, + path=PATH, sender_keyword='sender') + + def send_stack_cb(self, text, sender=None): + if sender == self.tube.get_unique_name(): + return + self.stack = text + self.stack_received_cb(text) + + @signal(dbus_interface=IFACE, signature='s') + def SendText(self, text): + self.stack = text |