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

This commit is contained in:
Dan Williams
2007-02-22 14:08:00 -05:00
41 changed files with 899 additions and 980 deletions
-211
View File
@@ -1,211 +0,0 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import os
import logging
import dbus
import dbus.service
import gtk
import gobject
import datetime
from sugar.presence import PresenceService
from sugar.datastore import datastore
from sugar import activity
from sugar import env
import sugar.util
ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
ACTIVITY_INTERFACE = "org.laptop.Activity"
def get_service_name(xid):
return ACTIVITY_SERVICE_NAME + '%d' % xid
def get_object_path(xid):
return ACTIVITY_SERVICE_PATH + "/%s" % xid
def get_service(xid):
bus = dbus.SessionBus()
proxy_obj = bus.get_object(get_service_name(xid), get_object_path(xid))
return dbus.Interface(proxy_obj, ACTIVITY_INTERFACE)
class ActivityDbusService(dbus.service.Object):
"""Base dbus service object that each Activity uses to export dbus methods.
The dbus service is separate from the actual Activity object so that we can
tightly control what stuff passes through the dbus python bindings."""
def __init__(self, activity):
xid = activity.window.xid
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(get_service_name(xid), bus=bus)
dbus.service.Object.__init__(self, bus_name, get_object_path(xid))
self._activity = activity
self._pservice = PresenceService.get_instance()
@dbus.service.method(ACTIVITY_INTERFACE)
def start(self, activity_id):
"""Start the activity in unshared mode."""
self._activity.start(activity_id)
@dbus.service.method(ACTIVITY_INTERFACE)
def join(self, activity_ps_path):
"""Join the activity specified by its presence service path."""
activity_ps = self._pservice.get(activity_ps_path)
return self._activity.join(activity_ps)
@dbus.service.method(ACTIVITY_INTERFACE)
def share(self):
"""Called by the shell to request the activity to share itself on the network."""
self._activity.share()
@dbus.service.method(ACTIVITY_INTERFACE)
def get_id(self):
"""Get the activity identifier"""
return self._activity.get_id()
@dbus.service.method(ACTIVITY_INTERFACE)
def get_type(self):
"""Get the activity type"""
return self._activity.get_type()
@dbus.service.method(ACTIVITY_INTERFACE)
def get_shared(self):
"""Returns True if the activity is shared on the mesh."""
return self._activity.get_shared()
@dbus.service.method(ACTIVITY_INTERFACE,
in_signature="sas", out_signature="b")
def execute(self, command, args):
return self._activity.execute(command, args)
class Activity(gtk.Window):
"""Base Activity class that all other Activities derive from."""
def __init__(self):
gtk.Window.__init__(self)
self.connect('destroy', self._destroy_cb)
#self.connect('notify::title', self._title_changed_cb)
self._shared = False
self._activity_id = None
self._service = None
#self._journal_object = None
self._pservice = PresenceService.get_instance()
self.realize()
group = gtk.Window()
group.realize()
self.window.set_group(group.window)
self._bus = ActivityDbusService(self)
def start(self, activity_id):
"""Start the activity."""
if self._activity_id != None:
logging.warning('The activity has been already started.')
return
self._activity_id = activity_id
#ds = datastore.get_instance()
#self._journal_object = ds.create('', {}, self._activity_id)
#
#date = datetime.datetime.now()
#self._journal_jobject.set_properties({'date' : date,
# 'title' : self.get_title()})
self.present()
# def get_journal_object(self):
# """Returns the journal object associated with the activity."""
# return self._journal_object
def get_type(self):
"""Gets the activity type."""
return env.get_bundle_service_name()
def get_default_type(self):
"""Gets the type of the default activity network service"""
return env.get_bundle_default_type()
def get_shared(self):
"""Returns TRUE if the activity is shared on the mesh."""
return self._shared
def get_id(self):
"""Get the unique activity identifier."""
return self._activity_id
def join(self, activity_ps):
"""Join an activity shared on the network."""
if self._activity_id != None:
logging.warning('The activity has been already started.')
return
self._activity_id = activity_ps.get_id()
self._shared = True
# Publish the default service, it's a copy of
# one of those we found on the network.
default_type = self.get_default_type()
services = activity_ps.get_services_of_type(default_type)
if len(services) > 0:
service = services[0]
addr = service.get_address()
port = service.get_port()
properties = service.get_published_values()
self._service = self._pservice.share_activity(
self, default_type, properties, addr, port)
else:
logging.error('Cannot join the activity')
#ds = datastore.get_instance()
#self._journal_object = ds.get_activity_object(self._activity_id)
self.present()
def share(self):
"""Share the activity on the network."""
logging.debug('Share activity %s on the network.' % self.get_id())
default_type = self.get_default_type()
self._service = self._pservice.share_activity(self, default_type)
self._shared = True
def execute(self, command, args):
"""Execute the given command with args"""
return False
def _destroy_cb(self, window):
if self._bus:
del self._bus
self._bus = None
if self._service:
self._pservice.unregister_service(self._service)
def _title_changed_cb(self, window, spec):
pass
# jobject = self.get_journal_object()
# if jobject:
# jobject.set_properties({'title' : self.props.title})
-127
View File
@@ -1,127 +0,0 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import os
import sys
import logging
import dbus
import dbus.service
import gobject
import gtk
from sugar.presence.PresenceService import PresenceService
from sugar.activity import Activity
from sugar.activity.bundle import Bundle
from sugar import logger
def get_path(activity_name):
"""Returns the activity path"""
return '/' + activity_name.replace('.', '/')
class ActivityFactory(dbus.service.Object):
"""Dbus service that takes care of creating new instances of an activity"""
def __init__(self, activity_type, activity_class):
self._activity_type = activity_type
self._activities = []
splitted_module = activity_class.rsplit('.', 1)
module_name = splitted_module[0]
class_name = splitted_module[1]
module = __import__(module_name)
for comp in module_name.split('.')[1:]:
module = getattr(module, comp)
if hasattr(module, 'start'):
module.start()
self._module = module
self._constructor = getattr(module, class_name)
bus = dbus.SessionBus()
factory = activity_type
bus_name = dbus.service.BusName(factory, bus = bus)
dbus.service.Object.__init__(self, bus_name, get_path(factory))
@dbus.service.method("com.redhat.Sugar.ActivityFactory")
def create(self):
activity = self._constructor()
self._activities.append(activity)
activity.connect('destroy', self._activity_destroy_cb)
return activity.window.xid
def _activity_destroy_cb(self, activity):
self._activities.remove(activity)
if hasattr(self._module, 'stop'):
self._module.stop()
if len(self._activities) == 0:
gtk.main_quit()
class ActivityCreationHandler(gobject.GObject):
__gsignals__ = {
'error': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
'success': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT]))
}
def __init__(self, activity_name):
gobject.GObject.__init__(self)
bus = dbus.SessionBus()
factory_name = activity_name
factory_path = get_path(factory_name)
proxy_obj = bus.get_object(factory_name, factory_path)
factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory")
factory.create(reply_handler=self._reply_handler, error_handler=self._error_handler)
def _reply_handler(self, xid):
bus = dbus.SessionBus()
proxy_obj = bus.get_object(Activity.get_service_name(xid),
Activity.get_object_path(xid))
activity = dbus.Interface(proxy_obj, Activity.ACTIVITY_INTERFACE)
self.emit('success', activity)
def _error_handler(self, err):
logging.debug("Couldn't create activity: %s" % err)
self.emit('error', err)
def create(activity_name):
"""Create a new activity from its name."""
return ActivityCreationHandler(activity_name)
def start_factory(activity_class, bundle_path):
"""Start the activity factory."""
bundle = Bundle(bundle_path)
logger.start(bundle.get_name())
os.environ['SUGAR_BUNDLE_PATH'] = bundle_path
os.environ['SUGAR_BUNDLE_SERVICE_NAME'] = bundle.get_service_name()
os.environ['SUGAR_BUNDLE_DEFAULT_TYPE'] = bundle.get_default_type()
factory = ActivityFactory(bundle.get_service_name(), activity_class)
+9 -6
View File
@@ -1,8 +1,11 @@
sugardir = $(pythondir)/sugar/activity
sugar_PYTHON = \
__init__.py \
Activity.py \
ActivityFactory.py \
bundle.py \
bundlebuilder.py \
sugar_PYTHON = \
__init__.py \
activity.py \
activityfactory.py \
activityfactoryservice.py \
activityhandle.py \
activityservice.py \
bundle.py \
bundlebuilder.py \
bundleregistry.py
+108
View File
@@ -0,0 +1,108 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import logging
import os
import gtk
from sugar.presence import PresenceService
from sugar.activity.activityservice import ActivityService
class Activity(gtk.Window):
"""Base Activity class that all other Activities derive from."""
def __init__(self, handle):
gtk.Window.__init__(self)
self.connect('destroy', self._destroy_cb)
self._shared = False
self._activity_id = handle.activity_id
self._pservice = PresenceService.get_instance()
service = handle.get_presence_service()
if service:
self._join(service)
self.realize()
group = gtk.Window()
group.realize()
self.window.set_group(group.window)
self._bus = ActivityService(self)
self.present()
def get_service_name(self):
"""Gets the activity service name."""
return os.environ['SUGAR_BUNDLE_SERVICE_NAME']
def get_default_type(self):
"""Gets the type of the default activity network service"""
return os.environ['SUGAR_BUNDLE_DEFAULT_TYPE']
def get_shared(self):
"""Returns TRUE if the activity is shared on the mesh."""
return self._shared
def get_id(self):
"""Get the unique activity identifier."""
return self._activity_id
def _join(self, service):
self._service = service
self._shared = True
# Publish the default service, it's a copy of
# one of those we found on the network.
default_type = self.get_default_type()
services = activity_ps.get_services_of_type(default_type)
if len(services) > 0:
service = services[0]
addr = service.get_address()
port = service.get_port()
properties = service.get_published_values()
self._service = self._pservice.share_activity(
self, default_type, properties, addr, port)
else:
logging.error('Cannot join the activity')
self.present()
def share(self):
"""Share the activity on the network."""
logging.debug('Share activity %s on the network.' % self.get_id())
default_type = self.get_default_type()
self._service = self._pservice.share_activity(self, default_type)
self._shared = True
def execute(self, command, args):
"""Execute the given command with args"""
return False
def _destroy_cb(self, window):
if self._bus:
del self._bus
self._bus = None
if self._service:
self._pservice.unregister_service(self._service)
def get_bundle_path():
return os.environ['SUGAR_BUNDLE_BUNDLE_PATH']
+102
View File
@@ -0,0 +1,102 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import logging
import dbus
import gobject
import gtk
from sugar.presence import PresenceService
from sugar.activity import bundleregistry
from sugar.activity.activityhandle import ActivityHandle
from sugar import util
_ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
_ACTIVITY_INTERFACE = "org.laptop.Activity"
def _find_activity_id():
pservice = PresenceService.get_instance()
# create a new unique activity ID
i = 0
act_id = None
while i < 10:
act_id = util.unique_id()
i += 1
# check through network activities
found = False
activities = pservice.get_activities()
for act in activities:
if act_id == act.get_id():
found = True
break
if found:
raise RuntimeError("Cannot generate unique activity id.")
return act_id
class ActivityCreationHandler(gobject.GObject):
__gsignals__ = {
'error': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
([gobject.TYPE_PYOBJECT])),
}
def __init__(self, service_name, activity_handle):
gobject.GObject.__init__(self)
self._service_name = service_name
self._activity_handle = activity_handle
registry = bundleregistry.get_registry()
bundle = registry.get_bundle(service_name)
bus = dbus.SessionBus()
proxy_obj = bus.get_object(service_name, bundle.get_object_path())
factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory")
factory.create(self._activity_handle.get_dict(),
reply_handler=self._reply_handler,
error_handler=self._error_handler)
def get_activity_id(self):
return self._activity_handle.activity_id
def _reply_handler(self, xid):
logging.debug("Activity created %s (%s)." %
(self._activity_handle.activity_id, self._service_name))
def _error_handler(self, err):
logging.debug("Couldn't create activity %s (%s): %s" %
(self._activity_handle.activity_id, self._service_name, err))
self.emit('error', err)
def create(service_name, activity_handle=None):
"""Create a new activity from its name."""
if not activity_handle:
activity_handle = ActivityHandle(_find_activity_id())
return ActivityCreationHandler(service_name, activity_handle)
def create_with_uri(service_name, uri):
"""Create a new activity and pass the uri as handle."""
activity_handle = ActivityHandle(_find_activity_id())
activity_handle.uri = uri
return ActivityCreationHandler(service_name, handle)
+97
View File
@@ -0,0 +1,97 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import os
import sys
from optparse import OptionParser
import gobject
import gtk
import dbus
import dbus.service
import dbus.glib
from sugar.activity.bundle import Bundle
from sugar.activity import activityhandle
from sugar import logger
# Work around for dbus mutex locking issue
gobject.threads_init()
dbus.glib.threads_init()
class ActivityFactoryService(dbus.service.Object):
"""D-Bus service that creates new instances of an activity"""
def __init__(self, service_name, activity_class):
self._activities = []
splitted_module = activity_class.rsplit('.', 1)
module_name = splitted_module[0]
class_name = splitted_module[1]
module = __import__(module_name)
for comp in module_name.split('.')[1:]:
module = getattr(module, comp)
if hasattr(module, 'start'):
module.start()
self._module = module
self._constructor = getattr(module, class_name)
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(service_name, bus = bus)
object_path = '/' + service_name.replace('.', '/')
dbus.service.Object.__init__(self, bus_name, object_path)
@dbus.service.method("com.redhat.Sugar.ActivityFactory", in_signature="a{ss}")
def create(self, handle):
activity_handle = activityhandle.create_from_dict(handle)
activity = self._constructor(activity_handle)
self._activities.append(activity)
activity.connect('destroy', self._activity_destroy_cb)
return activity.window.xid
def _activity_destroy_cb(self, activity):
self._activities.remove(activity)
if hasattr(self._module, 'stop'):
self._module.stop()
if len(self._activities) == 0:
gtk.main_quit()
def run(args):
"""Start the activity factory."""
parser = OptionParser()
parser.add_option("-p", "--bundle-path", dest="bundle_path",
help="path to the activity bundle")
(options, args) = parser.parse_args()
sys.path.insert(0, options.bundle_path)
bundle = Bundle(options.bundle_path)
logger.start(bundle.get_name())
os.environ['SUGAR_BUNDLE_PATH'] = options.bundle_path
os.environ['SUGAR_BUNDLE_SERVICE_NAME'] = bundle.get_service_name()
os.environ['SUGAR_BUNDLE_DEFAULT_TYPE'] = bundle.get_default_type()
factory = ActivityFactoryService(bundle.get_service_name(), args[0])
gtk.main()
+49
View File
@@ -0,0 +1,49 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
from sugar.presence import PresenceService
class ActivityHandle(object):
def __init__(self, activity_id):
self.activity_id = activity_id
self.pservice_id = None
self.uri = None
def get_presence_service(self):
if self.pservice_id:
pservice = PresenceService.get_instance()
return pservice.get_activity(self.pservice_id)
else:
return None
def get_dict(self):
result = { 'activity_id' : self.activity_id }
if self.pservice_id:
result['pservice_id'] = self.pservice_id
if self.uri:
result['uri'] = self.uri
return result
def create_from_dict(handle_dict):
result = ActivityHandle(handle_dict['activity_id'])
if handle_dict.has_key('pservice_id'):
result.pservice_id = handle_dict['pservice_id']
if handle_dict.has_key('uri'):
result.uri = handle_dict['uri']
return result
+68
View File
@@ -0,0 +1,68 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import dbus
import dbus.service
from sugar.presence import PresenceService
_ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
_ACTIVITY_INTERFACE = "org.laptop.Activity"
class ActivityService(dbus.service.Object):
"""Base dbus service object that each Activity uses to export dbus methods.
The dbus service is separate from the actual Activity object so that we can
tightly control what stuff passes through the dbus python bindings."""
def __init__(self, activity):
xid = activity.window.xid
service_name = _ACTIVITY_SERVICE_NAME + '%d' % xid
object_path = _ACTIVITY_SERVICE_PATH + "/%s" % xid
bus = dbus.SessionBus()
bus_name = dbus.service.BusName(service_name, bus=bus)
dbus.service.Object.__init__(self, bus_name, object_path)
self._activity = activity
@dbus.service.method(_ACTIVITY_INTERFACE)
def share(self):
"""Called by the shell to request the activity to share itself on the network."""
self._activity.share()
@dbus.service.method(_ACTIVITY_INTERFACE)
def get_id(self):
"""Get the activity identifier"""
return self._activity.get_id()
@dbus.service.method(_ACTIVITY_INTERFACE)
def get_service_name(self):
"""Get the activity service name"""
return self._activity.get_service_name()
@dbus.service.method(_ACTIVITY_INTERFACE)
def get_shared(self):
"""Returns True if the activity is shared on the mesh."""
return self._activity.get_shared()
@dbus.service.method(_ACTIVITY_INTERFACE,
in_signature="sas", out_signature="b")
def execute(self, command, args):
return self._activity.execute(command, args)
+21 -3
View File
@@ -1,8 +1,11 @@
import logging
import os
from ConfigParser import ConfigParser
from sugar import env
_PYTHON_FACTORY='sugar-activity-factory'
class Bundle:
"""Info about an activity bundle. Wraps the activity.info file."""
def __init__(self, path):
@@ -38,11 +41,18 @@ class Bundle:
self._valid = False
logging.error('%s must specify a name' % self._path)
if cp.has_option(section, 'exec'):
if cp.has_option(section, 'class'):
self._class = cp.get(section, 'class')
self._exec = '%s %s --bundle-path=%s' % (
os.path.join(env.get_shell_bin_dir(), _PYTHON_FACTORY),
self._class, self.get_path())
elif cp.has_option(section, 'exec'):
self._class = None
self._exec = cp.get(section, 'exec')
else:
self._exec = None
self._valid = False
logging.error('%s must specify an exec' % self._path)
logging.error('%s must specify exec or class' % self._path)
if cp.has_option(section, 'show_launcher'):
if cp.get(section, 'show_launcher') == 'no':
@@ -71,6 +81,10 @@ class Bundle:
"""Get the activity service name"""
return self._service_name
def get_object_path(self):
"""Get the path to the service object"""
return '/' + self._service_name.replace('.', '/')
def get_default_type(self):
"""Get the type of the main network service which tracks presence
and provides info about the activity, for example the title."""
@@ -90,6 +104,10 @@ class Bundle:
"""Get the command to execute to launch the activity factory"""
return self._exec
def get_class(self):
"""Get the main Activity class"""
return self._exec
def get_show_launcher(self):
"""Get whether there should be a visible launcher for the activity"""
return self._show_launcher
+13 -7
View File
@@ -11,13 +11,8 @@ class _ServiceManager(object):
self._path = env.get_user_service_dir()
def add(self, bundle):
name = bundle.get_service_name()
# FIXME evil hack. Probably need to fix Exec spec
full_exec = env.get_shell_bin_dir() + '/' + bundle.get_exec()
full_exec += ' ' + bundle.get_path()
util.write_service(name, full_exec, self._path)
util.write_service(bundle.get_service_name(),
bundle.get_exec(), self._path)
class BundleRegistry(gobject.GObject):
"""Service that tracks the available activity bundles"""
@@ -73,3 +68,14 @@ class BundleRegistry(gobject.GObject):
return True
else:
return False
def get_registry():
return _bundle_registry
_bundle_registry = BundleRegistry()
for path in env.get_data_dirs():
bundles_path = os.path.join(path, 'activities')
_bundle_registry.add_search_path(bundles_path)
_bundle_registry.add_search_path(env.get_user_activities_dir())
-18
View File
@@ -24,30 +24,12 @@ try:
except ImportError:
from sugar.__installed__ import *
def get_bundle_path():
if os.environ.has_key('SUGAR_BUNDLE_PATH'):
return os.environ['SUGAR_BUNDLE_PATH']
else:
return None
def is_emulator():
if os.environ.has_key('SUGAR_EMULATOR'):
if os.environ['SUGAR_EMULATOR'] == 'yes':
return True
return False
def get_bundle_service_name():
if os.environ.has_key('SUGAR_BUNDLE_SERVICE_NAME'):
return os.environ['SUGAR_BUNDLE_SERVICE_NAME']
else:
return None
def get_bundle_default_type():
if os.environ.has_key('SUGAR_BUNDLE_DEFAULT_TYPE'):
return os.environ['SUGAR_BUNDLE_DEFAULT_TYPE']
else:
return None
def get_profile_path():
if os.environ.has_key('SUGAR_PROFILE'):
profile_id = os.environ['SUGAR_PROFILE']
-1
View File
@@ -12,7 +12,6 @@ sugar_PYTHON = \
iconcolor.py \
label.py \
menu.py \
menuicon.py \
menushell.py \
optionmenu.py \
roundbox.py \
+86
View File
@@ -25,6 +25,7 @@ import cairo
import time
from sugar.graphics.iconcolor import IconColor
from sugar.graphics.timeline import Timeline
class _IconCacheIcon:
def __init__(self, name, color, now):
@@ -152,9 +153,17 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
self._icon_name = None
self._cache = False
self._handle = None
self._popup = None
self._hover_popup = False
self._timeline = Timeline(self)
self._timeline.add_tag('popup', 6, 6)
self._timeline.add_tag('before_popdown', 7, 7)
self._timeline.add_tag('popdown', 8, 8)
hippo.CanvasBox.__init__(self, **kwargs)
self.connect('motion-notify-event', self._motion_notify_event_cb)
self.connect('button-press-event', self._button_press_event_cb)
def _clear_buffers(self):
@@ -261,3 +270,80 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
def _button_press_event_cb(self, item, event):
item.emit_activated()
def get_popup(self):
return self._popup
def get_popup_context(self):
return None
def do_popup(self, current, n_frames):
if self._popup:
return
popup = self.get_popup()
if not popup:
return
popup_context = self.get_popup_context()
[x, y] = [None, None]
if popup_context:
try:
[x, y] = popup_context.get_position(self, popup)
except NotImplementedError:
pass
if [x, y] == [None, None]:
context = self.get_context()
[x, y] = context.translate_to_screen(self)
# TODO: Any better place to do this?
popup.props.box_width = max(popup.props.box_width,
self.get_width_request())
[width, height] = self.get_allocation()
y += height
position = [x, y]
popup.popup(x, y)
popup.connect('motion-notify-event',
self._popup_motion_notify_event_cb)
popup.connect('action-completed',
self._popup_action_completed_cb)
if popup_context:
popup_context.popped_up(popup)
self._popup = popup
def do_popdown(self, current, frame):
if self._popup:
self._popup.popdown()
popup_context = self.get_popup_context()
if popup_context:
popup_context.popped_down(self._popup)
self._popup = None
def popdown(self):
self._timeline.play('popdown', 'popdown')
def _motion_notify_event_cb(self, button, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self._timeline.play(None, 'popup')
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
if not self._hover_popup:
self._timeline.play('before_popdown', 'popdown')
def _popup_motion_notify_event_cb(self, popup, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self._hover_popup = True
self._timeline.play('popup', 'popup')
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
self._hover_popup = False
self._timeline.play('popdown', 'popdown')
def _popup_action_completed_cb(self, popup):
self.popdown()
+2 -88
View File
@@ -23,7 +23,6 @@ import hippo
from canvasicon import CanvasIcon
from iconcolor import IconColor
from sugar.graphics import units
from sugar.graphics.timeline import Timeline
from sugar import profile
STANDARD_SIZE = 0
@@ -49,100 +48,14 @@ class IconButton(CanvasIcon):
self._prelight_color = profile.get_color()
self._inactive_color = IconColor('#808080,#424242')
self._active = True
self._popup = None
self._hover_popup = False
CanvasIcon.__init__(self, icon_name=icon_name, cache=True,
color=self._normal_color)
self._set_size(STANDARD_SIZE)
self._timeline = Timeline(self)
self._timeline.add_tag('popup', 6, 6)
self._timeline.add_tag('before_popdown', 7, 7)
self._timeline.add_tag('popdown', 8, 8)
self.connect('motion-notify-event', self._motion_notify_event_cb)
self.connect('button-press-event', self._button_press_event_cb)
def get_popup(self):
return self._popup
def get_popup_context(self):
return None
def do_popup(self, current, n_frames):
if self._popup:
return
popup = self.get_popup()
if not popup:
return
popup_context = self.get_popup_context()
[x, y] = [None, None]
if popup_context:
try:
[x, y] = popup_context.get_position(self, popup)
except NotImplementedError:
pass
if [x, y] == [None, None]:
context = self.get_context()
#[x, y] = context.translate_to_screen(self)
[x, y] = context.translate_to_widget(self)
# TODO: Any better place to do this?
popup.props.box_width = max(popup.props.box_width,
self.get_width_request())
[width, height] = self.get_allocation()
y += height
position = [x, y]
popup.popup(x, y)
popup.connect('motion-notify-event',
self._popup_motion_notify_event_cb)
popup.connect('action-completed',
self._popup_action_completed_cb)
if popup_context:
popup_context.popped_up(popup)
self._popup = popup
def do_popdown(self, current, frame):
if self._popup:
self._popup.popdown()
popup_context = self.get_popup_context()
if popup_context:
popup_context.popped_down(self._popup)
self._popup = None
def popdown(self):
self._timeline.play('popdown', 'popdown')
def _motion_notify_event_cb(self, button, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self._timeline.play(None, 'popup')
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
if not self._hover_popup:
self._timeline.play('before_popdown', 'popdown')
def _popup_motion_notify_event_cb(self, popup, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self._hover_popup = True
self._timeline.play('popup', 'popup')
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
self._hover_popup = False
self._timeline.play('popdown', 'popdown')
def _popup_action_completed_cb(self, popup):
self.popdown()
def _set_size(self, size):
if size == SMALL_SIZE:
self.props.box_width = -1
@@ -173,8 +86,9 @@ class IconButton(CanvasIcon):
elif pspec.name == 'active':
return self._active
else:
return CanvasIcon.get_property(self, pspec)
return CanvasIcon.do_get_property(self, pspec)
def _button_press_event_cb(self, widget, event):
if self._active:
self.emit_activated()
return True
+78 -74
View File
@@ -14,94 +14,98 @@
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import sys
import gtk
import hippo
import gobject
from sugar.graphics.canvasicon import CanvasIcon
from sugar.graphics.popup import Popup
from sugar.graphics.roundbox import RoundBox
from sugar.graphics import color
from sugar.graphics import font
from sugar.graphics import units
class Menu(gtk.Window):
__gsignals__ = {
'action': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE, ([int])),
class MenuItem(hippo.CanvasBox):
__gtype_name__ = 'SugarMenuItem'
__gproperties__ = {
'action-id': (int, None, None,
0, sys.maxint, 0,
gobject.PARAM_READWRITE),
'label' : (str, None, None, None,
gobject.PARAM_READWRITE)
}
def __init__(self, title=None, content_box=None):
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
def __init__(self, action_id, label, icon_name=None, icon_color=None):
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
self._action_id = action_id
self.props.padding = 5
self.props.spacing = 5
canvas = hippo.Canvas()
self.add(canvas)
canvas.show()
if icon_name:
icon = CanvasIcon(icon_name=icon_name,
scale=units.SMALL_ICON_SCALE)
if icon_color:
icon.props.color = icon_color
self.append(icon)
self._root = hippo.CanvasBox()
canvas.set_root(self._root)
self._canvas_text = hippo.CanvasText(text=label)
self._canvas_text.props.color = color.LABEL_TEXT.get_int()
self._canvas_text.props.font_desc = font.DEFAULT.get_pango_desc()
self.append(self._canvas_text)
def do_set_property(self, pspec, value):
if pspec.name == 'action-id':
self._action_id = value
elif pspec.name == 'label':
self._canvas_text.props.text = value
else:
hippo.CanvasBox.do_set_property(self, pspec, value)
def do_get_property(self, pspec):
if pspec.name == 'action-id':
return self._action_id
elif pspec.name == 'label':
return self._canvas_text.props.text
else:
return hippo.CanvasBox.do_get_property(self, pspec)
class Menu(Popup):
__gtype_name__ = 'SugarMenu'
__gsignals__ = {
'action': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object]))
}
def __init__(self, title=None):
Popup.__init__(self)
self.props.background_color = color.MENU_BACKGROUND.get_int()
self.props.border_color = color.MENU_BORDER.get_int()
self.props.border = units.points_to_pixels(1)
if title:
self._title_item = hippo.CanvasText(text=title)
self._root.append(self._title_item)
else:
self._title_item = None
title_item = hippo.CanvasText(text=title)
title_item.props.color = color.LABEL_TEXT.get_int()
title_item.props.font_desc = font.DEFAULT.get_pango_desc()
self.append(title_item)
self.add_separator()
if content_box:
separator = self._create_separator()
self._root.append(separator)
self._root.append(content_box)
def add_item(self, item):
item.connect('button-press-event', self._item_button_press_event_cb)
self.append(item)
self._action_box = None
self._item_box = None
def remove_item(self, item):
self.remove(item)
def _create_separator(self):
separator = hippo.CanvasBox()
return separator
def add_separator(self):
box = hippo.CanvasBox()
box.props.background_color = color.MENU_SEPARATOR.get_int()
box.props.box_height = units.points_to_pixels(1)
self.append(box)
def _create_item_box(self):
if self._title_item:
separator = self._create_separator()
self._root.append(separator)
self._item_box = hippo.CanvasBox(
orientation=hippo.ORIENTATION_VERTICAL)
self._root.append(self._item_box)
def _create_action_box(self):
separator = self._create_separator()
self._root.append(separator)
self._action_box = hippo.CanvasBox(
orientation=hippo.ORIENTATION_HORIZONTAL)
self._root.append(self._action_box)
def add_item(self, label, action_id=None, wrap=False):
if not self._item_box:
self._create_item_box()
text = hippo.CanvasText(text=label)
if wrap:
text.set_property("size-mode", "wrap-word")
# FIXME need a way to make hippo items activable in python
if action_id:
text.connect('button-press-event', self._item_clicked_cb, action_id)
#text.connect('activated', self._action_clicked_cb, action_id)
self._item_box.append(text)
def add_action(self, icon, action_id):
if not self._action_box:
self._create_action_box()
icon.connect('activated', self._action_clicked_cb, action_id)
self._action_box.append(icon)
def remove_action(self, icon):
self._action_box.remove(icon)
def _item_clicked_cb(self, icon, event, action):
self.emit('action', action)
def _action_clicked_cb(self, icon, action):
self.emit('action', action)
def set_title(self, title):
self._title_item.set_property('text', title)
def _item_button_press_event_cb(self, menu_item, event):
self.emit('action', menu_item)
-80
View File
@@ -1,80 +0,0 @@
# Copyright (C) 2006, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
import hippo
import gobject
import logging
from sugar.graphics.canvasicon import CanvasIcon
from sugar.graphics.timeline import Timeline
class MenuIcon(CanvasIcon):
def __init__(self, menu_shell, **kwargs):
CanvasIcon.__init__(self, **kwargs)
self._menu_shell = menu_shell
self._menu = None
self._hover_menu = False
self._timeline = Timeline(self)
self._timeline.add_tag('popup', 6, 6)
self._timeline.add_tag('before_popdown', 7, 7)
self._timeline.add_tag('popdown', 8, 8)
self.connect('motion-notify-event', self._motion_notify_event_cb)
def do_popup(self, current, n_frames):
if self._menu:
return
self._menu = self.create_menu()
self._menu.connect('enter-notify-event',
self._menu_enter_notify_event_cb)
self._menu.connect('leave-notify-event',
self._menu_leave_notify_event_cb)
[x, y] = self._menu_shell.get_position(self._menu, self)
self._menu.move(x, y)
self._menu.show()
self._menu_shell.set_active(self)
def do_popdown(self, current, frame):
if self._menu:
self._menu.destroy()
self._menu = None
self._menu_shell.set_active(None)
def popdown(self):
self._timeline.play('popdown', 'popdown')
def _motion_notify_event_cb(self, item, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
self._timeline.play(None, 'popup')
elif event.detail == hippo.MOTION_DETAIL_LEAVE:
if not self._hover_menu:
self._timeline.play('before_popdown', 'popdown')
def _menu_enter_notify_event_cb(self, widget, event):
self._hover_menu = True
self._timeline.play('popup', 'popup')
def _menu_leave_notify_event_cb(self, widget, event):
self._hover_menu = False
self._timeline.play('popdown', 'popdown')
+29 -67
View File
@@ -24,70 +24,27 @@ import hippo
from sugar.graphics import units
from sugar.graphics.roundbox import RoundBox
from sugar.graphics.menu import Menu, MenuItem
from sugar.graphics import iconbutton
from sugar.graphics import color
from sugar.graphics import font
from sugar.graphics.canvasicon import CanvasIcon
class Menu(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarMenu'
__gsignals__ = {
'action': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object]))
}
class _Menu(Menu):
def __init__(self):
hippo.CanvasBox.__init__(self)
self.props.background_color = color.MENU_BACKGROUND.get_int()
self.props.border_color = color.MENU_BORDER.get_int()
self.props.border = units.points_to_pixels(1)
self._window = None
def add_item(self, action_id, label, icon_name=None, icon_color=None):
box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
box.props.padding = 5
box.props.spacing = 5
if icon_name:
icon = CanvasIcon(icon_name=icon_name,
scale=units.SMALL_ICON_SCALE)
if icon_color:
icon.props.color = icon_color
box.append(icon)
canvas_text = hippo.CanvasText()
canvas_text.props.text = label
canvas_text.props.color = color.LABEL_TEXT.get_int()
canvas_text.props.font_desc = font.DEFAULT.get_pango_desc()
box.append(canvas_text)
box.connect('button-press-event', self._item_button_press_event_cb,
[action_id, label])
self.append(box)
def add_separator(self):
box = hippo.CanvasBox()
box.props.background_color = color.MENU_SEPARATOR.get_int()
box.props.box_height = units.points_to_pixels(1)
self.append(box)
def show(self, x, y):
if not self._window:
self._window = hippo.CanvasWindow(gtk.WINDOW_POPUP)
self._window.move(x, y)
self._window.set_root(self)
self._window.show()
def hide(self):
if self._window:
self._window.destroy()
self._window = None
def _item_button_press_event_cb(self, item, event, data):
self.emit('action', data)
self.hide()
Menu.__init__(self)
self._is_visible = False
def is_visible(self):
return self._window != None
return self._is_visible
def popup(self, x, y):
Menu.popup(self, x, y)
self._is_visible = True
def popdown(self):
Menu.popdown(self)
self._is_visible = False
class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarOptionMenu'
@@ -122,8 +79,9 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
arrow.props.xalign = hippo.ALIGNMENT_START
self._round_box.append(arrow)
self._menu = Menu()
self._menu = _Menu()
self._menu.connect('action', self._menu_action_cb)
self._menu.connect('action-completed', self._menu_action_completed_cb)
self.connect('button-press-event', self._button_press_event_cb)
@@ -135,33 +93,37 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
if pspec.name == 'value':
return self._value
def add_option(self, action_id, label, icon_name=None, icon_color=None):
def add_item(self, menu_item):
if not self._value:
self._value = action_id
self._canvas_text.props.text = label
self._value = menu_item.props.action_id
self._canvas_text.props.text = menu_item.props.label
self._menu.add_item(action_id, label, icon_name, icon_color)
self._menu.add_item(menu_item)
def add_separator(self):
self._menu.add_separator()
def _button_press_event_cb(self, box, event):
if self._menu.is_visible():
self._menu.hide()
self._menu.popdown()
else:
context = self._round_box.get_context()
#[x, y] = context.translate_to_screen(self._round_box)
[x, y] = context.translate_to_widget(self._round_box)
[x, y] = context.translate_to_screen(self._round_box)
# TODO: Any better place to do this?
self._menu.props.box_width = self.get_width_request()
[width, height] = self._round_box.get_allocation()
self._menu.show(x, y + height)
self._menu.popup(x, y + height)
def _menu_action_cb(self, menu, data):
[action_id, label] = data
def _menu_action_cb(self, menu, menu_item):
action_id = menu_item.props.action_id
label = menu_item.props.label
if action_id != self._value:
self._value = action_id
self._canvas_text.props.text = label
self.emit('changed')
def _menu_action_completed_cb(self, menu):
self._menu.popdown()
+3 -38
View File
@@ -21,13 +21,6 @@ import gobject
import gtk
import hippo
from sugar.graphics import units
from sugar.graphics.roundbox import RoundBox
from sugar.graphics import button
from sugar.graphics import color
from sugar.graphics import font
from sugar.graphics.canvasicon import CanvasIcon
class Popup(hippo.CanvasBox, hippo.CanvasItem):
__gtype_name__ = 'SugarPopup'
@@ -35,38 +28,10 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem):
'action-completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
}
def __init__(self, title):
def __init__(self):
hippo.CanvasBox.__init__(self)
self.props.background_color = color.MENU_BACKGROUND.get_int()
self.props.border_color = color.MENU_BORDER.get_int()
self.props.border = units.points_to_pixels(1)
self._window = None
def add_item(self, action_id, label, icon_name=None, icon_color=None):
box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL)
box.props.padding = 5
box.props.spacing = 5
if icon_name:
icon = CanvasIcon(icon_name=icon_name,
scale=units.SMALL_ICON_SCALE)
if icon_color:
icon.props.color = icon_color
box.append(icon)
canvas_text = hippo.CanvasText()
canvas_text.props.text = label
canvas_text.props.color = color.LABEL_TEXT.get_int()
canvas_text.props.font_desc = font.DEFAULT.get_pango_desc()
box.append(canvas_text)
box.connect('button-press-event', self._item_button_press_event_cb)
self.append(box)
def add_separator(self):
box = hippo.CanvasBox()
box.props.background_color = color.MENU_SEPARATOR.get_int()
box.props.box_height = units.points_to_pixels(1)
self.append(box)
self.connect('button-press-event', self._button_press_event_cb)
def popup(self, x, y):
if not self._window:
@@ -80,5 +45,5 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem):
self._window.destroy()
self._window = None
def _item_button_press_event_cb(self, item, event):
def _button_press_event_cb(self, menu, event):
self.emit('action-completed')