Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar

This commit is contained in:
Marco Pesenti Gritti 2007-04-14 10:00:00 +02:00
commit 845575c708
9 changed files with 194 additions and 15 deletions

View File

@ -18,6 +18,7 @@
import gobject
import dbus, dbus.service
from sugar import util
import logging
from telepathy.interfaces import (CHANNEL_INTERFACE)
@ -228,22 +229,27 @@ class Activity(DBusGObject):
# Not for us
return
(sigid, async_cb, async_err_cb) = userdata
(sigid, owner, async_cb, async_err_cb) = userdata
self._tp.disconnect(sigid)
if exc:
logging.debug("Share of activity %s failed: %s" % (self._id, exc))
async_err_cb(exc)
else:
self._handle_share_join(tp, text_channel)
self.send_properties()
owner.add_activity(self)
async_cb(dbus.ObjectPath(self._object_path))
logging.debug("Share of activity %s succeeded." % self._id)
def _share(self, (async_cb, async_err_cb)):
def _share(self, (async_cb, async_err_cb), owner):
logging.debug("Starting share of activity %s" % self._id)
if self._joined:
async_err_cb(RuntimeError("Already shared activity %s" % self.props.id))
return
sigid = self._tp.connect('activity-shared', self._shared_cb)
self._tp.share_activity(self.props.id, (sigid, async_cb, async_err_cb))
self._tp.share_activity(self.props.id, (sigid, owner, async_cb, async_err_cb))
logging.debug("done with share attempt %s" % self._id)
def _joined_cb(self, tp, activity_id, text_channel, exc, userdata):
if activity_id != self.props.id:

View File

@ -169,7 +169,10 @@ class PresenceService(dbus.service.Object):
del self._activities[activity.props.id]
def _buddy_activities_changed(self, tp, contact_handle, activities):
logging.debug("------------activities changed-------------")
acts = []
for act in activities:
acts.append(str(act))
logging.debug("Handle %s activities changed: %s" % (contact_handle, acts))
buddies = self._handles_buddies[tp]
buddy = buddies.get(contact_handle)
@ -306,7 +309,7 @@ class PresenceService(dbus.service.Object):
id=actid, type=atype, name=name, color=color, local=True)
activity.connect("validity-changed", self._activity_validity_changed_cb)
self._activities[actid] = activity
activity._share(callbacks)
activity._share(callbacks, self._owner)
# local activities are valid at creation by definition, but we can't
# connect to the activity's validity-changed signal until its already

View File

@ -66,8 +66,7 @@ class ActivityHost:
return self._activity.execute(command, dbus.Array(args))
def share(self):
self._activity.share()
self._chat_widget.share()
self._activity.share(ignore_reply=True)
def invite(self, buddy):
pass

View File

@ -0,0 +1,47 @@
"""Activity implementation code for Sugar-based activities
Each activity within the OLPC environment must provide two
dbus services. The first, patterned after the
sugar.activity.activityfactory.ActivityFactory
class is responsible for providing a "create" method which
takes a small dictionary with values corresponding to a
sugar.activity.activityhandle.ActivityHandle
describing an individual instance of the activity. The
ActivityFactory service is registered with dbus using the
global
sugar.activity.bundleregistry.BundleRegistry
service, which creates dbus .service files in a well known
directory. Those files tell dbus what executable to run
in order to load the ActivityFactory which will provide
the creation service.
Each activity so registered is described by a
sugar.activity.bundle.Bundle
instance, which parses a specially formatted activity.info
file (stored in the activity directory's ./activity
subdirectory). The
sugar.activity.bundlebuilder
module provides facilities for the standard setup.py module
which produces and registers bundles from activity source
directories.
Once instantiated by the ActivityFactory's create method,
each activity must provide an introspection API patterned
after the
sugar.activity.activityservice.ActivityService
class. This class allows for querying the ID of the root
window, requesting sharing across the network, and basic
"what type of application are you" queries.
"""

View File

@ -62,6 +62,7 @@ class Activity(Window, gtk.Container):
self._activity_id = handle.activity_id
self._pservice = presenceservice.get_instance()
self._service = None
self._share_sigid = None
service = handle.get_presence_service()
if service:
@ -100,11 +101,21 @@ class Activity(Window, gtk.Container):
self._service.join()
self.present()
def share(self):
"""Share the activity on the network."""
logging.debug('Share activity %s on the network.' % self.get_id())
self._service = self._pservice.share_activity(self)
def _share_cb(self, ps, success, service, err):
self._pservice.disconnect(self._share_sigid)
self._share_sigid = None
if success:
logging.debug('Share of activity %s successful.' % self.get_id())
self._service = service
self._shared = True
else:
logging.debug('Share of activity %s failed: %s.' % (self.get_id(), err))
def share(self):
"""Request that the activity be shared on the network."""
logging.debug('Requesting share of activity %s.' % self.get_id())
self._share_sigid = self._pservice.connect("activity-shared", self._share_cb)
self._pservice.share_activity(self)
def execute(self, command, args):
"""Execute the given command with args"""

View File

@ -99,6 +99,8 @@ class ActivityFactoryService(dbus.service.Object):
handle -- sugar.activity.activityhandle.ActivityHandle
compatible dictionary providing the instance-specific
values for the new instance
returns xid for the created instance' root window
"""
activity_handle = activityhandle.create_from_dict(handle)
activity = self._constructor(activity_handle)

View File

@ -1 +1,6 @@
"""Sugar's web-browser activity
XUL Runner and gtkmozembed and is produced by the PyGTK
.defs system.
"""
from sugar.browser._sugarbrowser import *

View File

@ -1,3 +1,4 @@
"""UI class to access system-level clipboard object"""
import logging
import dbus
import gobject
@ -14,7 +15,16 @@ DBUS_INTERFACE = "org.laptop.Clipboard"
DBUS_PATH = "/org/laptop/Clipboard"
class ClipboardService(gobject.GObject):
"""GUI interfaces for the system clipboard dbus service
This object is used to provide convenient access to the clipboard
service (see source/services/clipboard/clipboardservice.py). It
provides utility methods for adding/getting/removing objects from
the clipboard as well as generating events when such events occur.
Meaning is source/services/clipboard/clipboardobject.py
objects when describing "objects" on the clipboard.
"""
__gsignals__ = {
'object-added': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([str, str])),
@ -25,6 +35,11 @@ class ClipboardService(gobject.GObject):
}
def __init__(self):
"""Initialise the ClipboardService instance
If the service is not yet active in the background uses
a signal watcher to connect when the service appears.
"""
gobject.GObject.__init__(self)
self._dbus_service = None
@ -44,6 +59,7 @@ class ClipboardService(gobject.GObject):
logging.debug(exception)
def _connect_clipboard_signals(self):
"""Connect dbus signals to our GObject signal generating callbacks"""
bus = dbus.SessionBus()
if not self._connected:
proxy_obj = bus.get_object(DBUS_SERVICE, DBUS_PATH)
@ -59,46 +75,124 @@ class ClipboardService(gobject.GObject):
bus.remove_signal_receiver(self._nameOwnerChangedHandler)
def _name_owner_changed_cb(self, name, old, new):
"""On backend service creation, connect to the server"""
if not old and new:
# ClipboardService started up
self._connect_clipboard_signals()
def _object_added_cb(self, object_id, name):
"""Emit an object-added GObject event when dbus event arrives"""
self.emit('object-added', str(object_id), name)
def _object_deleted_cb(self, object_id):
"""Emit an object-deleted GObject event when dbus event arrives"""
self.emit('object-deleted', str(object_id))
def _object_state_changed_cb(self, object_id, values):
"""Emit an object-state-changed GObject event when dbus event arrives
GObject event has:
object_id
name
percent
icon
preview
activity
From the ClipboardObject instance which is being described.
"""
self.emit('object-state-changed', str(object_id), values[NAME_KEY],
values[PERCENT_KEY], values[ICON_KEY], values[PREVIEW_KEY],
values[ACTIVITY_KEY])
def add_object(self, name):
"""Add a new object to the path
returns dbus path-name for the new object's cliboard service,
this is used for all future references to the cliboard object.
Note:
That service is actually provided by the clipboard
service object, not the ClipboardObject
"""
return str(self._dbus_service.add_object(name))
def add_object_format(self, object_id, formatType, data, on_disk):
"""Annotate given object on the clipboard with new information
object_id -- dbus path as returned from add_object
formatType -- XXX what should this be? mime type?
data -- storage format for the clipped object?
on_disk -- whether the data is on-disk (non-volatile) or in
memory (volatile)
Last three arguments are just passed directly to the
clipboardobject.Format instance on the server side.
returns None
"""
self._dbus_service.add_object_format(dbus.ObjectPath(object_id),
formatType,
data,
on_disk)
def delete_object(self, object_id):
"""Remove the given object from the clipboard
object_id -- dbus path as returned from add_object
"""
self._dbus_service.delete_object(dbus.ObjectPath(object_id))
def set_object_percent(self, object_id, percent):
"""Set the "percentage" for the given clipboard object
object_id -- dbus path as returned from add_object
percentage -- numeric value from 0 to 100 inclusive
Object percentages which are set to 100% trigger "file-completed"
operations, see the backend ClipboardService's
_handle_file_completed method for details.
returns None
"""
self._dbus_service.set_object_percent(dbus.ObjectPath(object_id), percent)
def get_object(self, object_id):
"""Retrieve the clipboard object structure for given object
object_id -- dbus path as returned from add_object
Retrieves the metadata description of a given object, but
*not* the data for the object. Use get_object_data passing
one of the values in the FORMATS_KEY value in order to
retrieve the data.
returns dictionary with
NAME_KEY: str,
PERCENT_KEY: number,
ICON_KEY: str,
PREVIEW_KEY: XXX what is it?,
ACTIVITY_KEY: source activity id,
FORMATS_KEY: list of XXX what is it?
"""
return self._dbus_service.get_object(dbus.ObjectPath(object_id),)
def get_object_data(self, object_id, formatType):
"""Retrieve object's data in the given formatType
object_id -- dbus path as returned from add_object
formatType -- format specifier XXX of what description
returns data as a string
"""
return self._dbus_service.get_object_data(dbus.ObjectPath(object_id),
formatType,
byte_arrays=True)
_clipboard_service = None
def get_instance():
"""Retrieve this process's interface to the clipboard service"""
global _clipboard_service
if not _clipboard_service:
_clipboard_service = ClipboardService()

View File

@ -16,6 +16,7 @@
# Boston, MA 02111-1307, USA.
import dbus, dbus.glib, gobject
import logging
import buddy, activity
@ -59,7 +60,10 @@ class PresenceService(gobject.GObject):
'activity-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'activity-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
([gobject.TYPE_PYOBJECT])),
'activity-shared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT, gobject.TYPE_PYOBJECT,
gobject.TYPE_PYOBJECT]))
}
_PS_BUDDY_OP = DBUS_PATH + "/Buddies/"
@ -178,12 +182,20 @@ class PresenceService(gobject.GObject):
return None
return self._new_object(owner_op)
def _share_activity_cb(self, activity, op):
self.emit("activity-shared", True, self._new_object(op), None)
def _share_activity_error_cb(self, activity, err):
logging.debug("Error sharing activity %s: %s" % (activity.get_id(), err))
self.emit("activity-shared", False, None, err)
def share_activity(self, activity, properties={}):
actid = activity.get_id()
atype = activity.get_service_name()
name = activity.props.title
serv_op = self._ps.ShareActivity(actid, atype, name, properties)
return self._new_object(serv_op)
self._ps.ShareActivity(actid, atype, name, properties,
reply_handler=lambda *args: self._share_activity_cb(activity, *args),
error_handler=lambda *args: self._share_activity_error_cb(activity, *args))
class _MockPresenceService(gobject.GObject):