Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
This commit is contained in:
commit
3b863f45a8
@ -17,9 +17,11 @@
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <gdk/gdkx.h>
|
||||
|
||||
#include "sugar-utils.h"
|
||||
|
||||
gint
|
||||
sugar_get_screen_dpi(void)
|
||||
{
|
||||
@ -27,8 +29,8 @@ sugar_get_screen_dpi(void)
|
||||
if (val) {
|
||||
char *e;
|
||||
double d = strtod(val, &e);
|
||||
if (e != val)
|
||||
return round(d);
|
||||
if (d > 0.0)
|
||||
return (int)(d+0.5);
|
||||
}
|
||||
|
||||
return 96;
|
||||
|
@ -29,7 +29,7 @@ class TextFileType(FileType):
|
||||
return _('Text snippet')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-xbook'
|
||||
return 'theme:object-text'
|
||||
|
||||
def get_preview(self):
|
||||
for format, data in self._formats.iteritems():
|
||||
@ -57,7 +57,7 @@ class ImageFileType(FileType):
|
||||
return _('Image')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-sketch'
|
||||
return 'theme:object-image'
|
||||
|
||||
def get_preview(self):
|
||||
return ''
|
||||
@ -77,7 +77,7 @@ class UriFileType(FileType):
|
||||
return _('Web Page')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-web'
|
||||
return 'theme:object-link'
|
||||
|
||||
def get_preview(self):
|
||||
for format, data in self._formats.iteritems():
|
||||
@ -103,7 +103,7 @@ class PdfFileType(FileType):
|
||||
return _('PDF file')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-xbook'
|
||||
return 'theme:object-text'
|
||||
|
||||
def get_preview(self):
|
||||
return ''
|
||||
@ -123,7 +123,7 @@ class MsWordFileType(FileType):
|
||||
return _('MS Word file')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-abiword'
|
||||
return 'theme:object-text'
|
||||
|
||||
def get_preview(self):
|
||||
return ''
|
||||
@ -143,7 +143,7 @@ class RtfFileType(FileType):
|
||||
return _('RTF file')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-abiword'
|
||||
return 'theme:object-text'
|
||||
|
||||
def get_preview(self):
|
||||
return ''
|
||||
@ -163,7 +163,7 @@ class OOTextFileType(FileType):
|
||||
return _('OpenOffice text file')
|
||||
|
||||
def get_icon(self):
|
||||
return 'theme:activity-abiword'
|
||||
return 'theme:object-text'
|
||||
|
||||
def get_preview(self):
|
||||
return ''
|
||||
|
@ -18,6 +18,7 @@ import gobject
|
||||
|
||||
from sugar.graphics.iconcolor import IconColor
|
||||
from sugar.presence import PresenceService
|
||||
from sugar.activity import bundleregistry
|
||||
from model.BuddyModel import BuddyModel
|
||||
|
||||
class ActivityModel:
|
||||
@ -53,12 +54,12 @@ class MeshModel(gobject.GObject):
|
||||
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
|
||||
}
|
||||
|
||||
def __init__(self, bundle_registry):
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._activities = {}
|
||||
self._buddies = {}
|
||||
self._bundle_registry = bundle_registry
|
||||
self._bundle_registry = bundleregistry.get_registry()
|
||||
|
||||
self._pservice = PresenceService.get_instance()
|
||||
self._pservice.connect("service-appeared",
|
||||
|
@ -44,8 +44,6 @@ class ShellModel(gobject.GObject):
|
||||
self._current_activity = None
|
||||
self._state = self.STATE_RUNNING
|
||||
|
||||
self._bundle_registry = BundleRegistry()
|
||||
|
||||
PresenceService.start()
|
||||
self._pservice = PresenceService.get_instance()
|
||||
|
||||
@ -53,16 +51,10 @@ class ShellModel(gobject.GObject):
|
||||
self._owner.announce()
|
||||
|
||||
self._friends = Friends()
|
||||
self._mesh = MeshModel(self._bundle_registry)
|
||||
self._home = HomeModel(self._bundle_registry)
|
||||
self._mesh = MeshModel()
|
||||
self._home = HomeModel()
|
||||
self._devices = DevicesModel()
|
||||
|
||||
for path in env.get_data_dirs():
|
||||
bundles_path = os.path.join(path, 'activities')
|
||||
self._bundle_registry.add_search_path(bundles_path)
|
||||
|
||||
self._bundle_registry.add_search_path(env.get_user_activities_dir())
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
if pspec.name == 'state':
|
||||
self._state = value
|
||||
@ -71,9 +63,6 @@ class ShellModel(gobject.GObject):
|
||||
if pspec.name == 'state':
|
||||
return self._state
|
||||
|
||||
def get_bundle_registry(self):
|
||||
return self._bundle_registry
|
||||
|
||||
def get_mesh(self):
|
||||
return self._mesh
|
||||
|
||||
|
@ -15,14 +15,18 @@
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
import time
|
||||
import gobject
|
||||
import logging
|
||||
|
||||
import gobject
|
||||
import dbus
|
||||
|
||||
from sugar.graphics.iconcolor import IconColor
|
||||
from sugar.presence import PresenceService
|
||||
from sugar.activity import Activity
|
||||
from sugar import profile
|
||||
|
||||
_ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
|
||||
_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
|
||||
|
||||
class HomeActivity(gobject.GObject):
|
||||
__gsignals__ = {
|
||||
'launch-timeout': (gobject.SIGNAL_RUN_FIRST,
|
||||
@ -69,16 +73,22 @@ class HomeActivity(gobject.GObject):
|
||||
|
||||
self._window = window
|
||||
self._xid = window.get_xid()
|
||||
self._service = Activity.get_service(window.get_xid())
|
||||
|
||||
bus = dbus.SessionBus()
|
||||
self._service = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % self._xid,
|
||||
_ACTIVITY_SERVICE_PATH + "/%s" % self._xid)
|
||||
|
||||
# verify id and type details
|
||||
act_id = self._service.get_id()
|
||||
if act_id != self._id:
|
||||
raise RuntimeError("Activity's real ID (%s) didn't match expected (%s)." % (act_id, self._id))
|
||||
act_type = self._service.get_type()
|
||||
act_type = self._service.get_service_name()
|
||||
if act_type != self._type:
|
||||
raise RuntimeError("Activity's real type (%s) didn't match expected (%s)." % (act_type, self._type))
|
||||
|
||||
def get_service(self):
|
||||
return self._service
|
||||
|
||||
def get_title(self):
|
||||
if not self._launched:
|
||||
raise RuntimeError("Activity is still launching.")
|
||||
|
@ -18,9 +18,13 @@ import logging
|
||||
|
||||
import gobject
|
||||
import wnck
|
||||
import dbus
|
||||
|
||||
from model.homeactivity import HomeActivity
|
||||
from sugar.activity import Activity
|
||||
from sugar.activity import bundleregistry
|
||||
|
||||
_ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
|
||||
_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
|
||||
|
||||
class HomeModel(gobject.GObject):
|
||||
|
||||
@ -39,11 +43,11 @@ class HomeModel(gobject.GObject):
|
||||
([gobject.TYPE_PYOBJECT]))
|
||||
}
|
||||
|
||||
def __init__(self, bundle_registry):
|
||||
def __init__(self):
|
||||
gobject.GObject.__init__(self)
|
||||
|
||||
self._activities = {}
|
||||
self._bundle_registry = bundle_registry
|
||||
self._bundle_registry = bundleregistry.get_registry()
|
||||
self._current_activity = None
|
||||
|
||||
screen = wnck.screen_get_default()
|
||||
@ -114,7 +118,10 @@ class HomeModel(gobject.GObject):
|
||||
self.emit('active-activity-changed', self._current_activity)
|
||||
|
||||
def _add_activity(self, window):
|
||||
act_service = Activity.get_service(window.get_xid())
|
||||
bus = dbus.SessionBus()
|
||||
xid = window.get_xid()
|
||||
act_service = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % xid,
|
||||
_ACTIVITY_SERVICE_PATH + "/%s" % xid)
|
||||
act_id = act_service.get_id()
|
||||
|
||||
activity = None
|
||||
@ -123,7 +130,7 @@ class HomeModel(gobject.GObject):
|
||||
else:
|
||||
# activity got lost, took longer to launch than we allow,
|
||||
# or it was launched by something other than the shell
|
||||
act_type = act_service.get_type()
|
||||
act_type = act_service.get_service_name()
|
||||
bundle = self._bundle_registry.get_bundle(act_type)
|
||||
if not bundle:
|
||||
raise RuntimeError("No bundle for activity type '%s'." % act_type)
|
||||
|
@ -20,7 +20,7 @@ import sys
|
||||
import os
|
||||
import gobject
|
||||
|
||||
from sugar.activity import ActivityFactory
|
||||
from sugar.activity import activityfactory
|
||||
from sugar import env
|
||||
from sugar import util
|
||||
|
||||
@ -40,7 +40,7 @@ os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_name
|
||||
|
||||
loop = gobject.MainLoop()
|
||||
|
||||
handler = ActivityFactory.create(sys.argv[1])
|
||||
handler = activityfactory.create(sys.argv[1])
|
||||
handler.connect('success', _success_cb, loop)
|
||||
handler.connect('error', _error_cb, loop)
|
||||
|
||||
|
@ -18,21 +18,6 @@
|
||||
|
||||
import sys
|
||||
|
||||
import pygtk
|
||||
pygtk.require('2.0')
|
||||
import gobject
|
||||
import gtk
|
||||
from sugar.activity import activityfactoryservice
|
||||
|
||||
import dbus.glib
|
||||
|
||||
# Work around for dbus mutex locking issue
|
||||
gobject.threads_init()
|
||||
dbus.glib.threads_init()
|
||||
|
||||
from sugar.activity import ActivityFactory
|
||||
|
||||
sys.path.insert(0, sys.argv[2])
|
||||
|
||||
ActivityFactory.start_factory(sys.argv[1], sys.argv[2])
|
||||
|
||||
gtk.main()
|
||||
activityfactoryservice.run(sys.argv)
|
||||
|
@ -17,7 +17,6 @@
|
||||
import gtk
|
||||
import dbus
|
||||
|
||||
from sugar.activity import Activity
|
||||
from sugar.p2p import Stream
|
||||
from sugar.p2p import network
|
||||
from sugar.chat import ActivityChat
|
||||
@ -42,7 +41,7 @@ class ActivityHost:
|
||||
self._model = model
|
||||
self._id = model.get_id()
|
||||
self._window = model.get_window()
|
||||
self._activity = Activity.get_service(self.get_xid())
|
||||
self._activity = model.get_service()
|
||||
self._gdk_window = gtk.gdk.window_foreign_new(self.get_xid())
|
||||
|
||||
try:
|
||||
|
@ -14,13 +14,13 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
from sugar.graphics.menuicon import MenuIcon
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from view.BuddyMenu import BuddyMenu
|
||||
|
||||
class BuddyIcon(MenuIcon):
|
||||
class BuddyIcon(CanvasIcon):
|
||||
def __init__(self, shell, menu_shell, buddy):
|
||||
MenuIcon.__init__(self, menu_shell, icon_name='theme:stock-buddy',
|
||||
color=buddy.get_color())
|
||||
CanvasIcon.__init__(self, icon_name='theme:stock-buddy',
|
||||
color=buddy.get_color())
|
||||
|
||||
self._shell = shell
|
||||
self._buddy = buddy
|
||||
@ -35,13 +35,16 @@ class BuddyIcon(MenuIcon):
|
||||
def set_popup_distance(self, distance):
|
||||
self._popup_distance = distance
|
||||
|
||||
def create_menu(self):
|
||||
def get_popup(self):
|
||||
menu = BuddyMenu(self._shell, self._buddy)
|
||||
menu.connect('action', self._popup_action_cb)
|
||||
return menu
|
||||
|
||||
def _popup_action_cb(self, popup, action):
|
||||
self.popdown()
|
||||
def get_popup_context(self):
|
||||
return self._shell.get_popup_context()
|
||||
|
||||
def _popup_action_cb(self, popup, menu_item):
|
||||
action = menu_item.props.action_id
|
||||
|
||||
friends = self._shell.get_model().get_friends()
|
||||
if action == BuddyMenu.ACTION_REMOVE_FRIEND:
|
||||
|
@ -13,18 +13,18 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
from gettext import gettext as _
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.menu import Menu
|
||||
from sugar.graphics.menu import Menu, MenuItem
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar.graphics import units
|
||||
from sugar.presence import PresenceService
|
||||
import _sugar
|
||||
|
||||
_ICON_SIZE = 75
|
||||
|
||||
class BuddyMenu(Menu):
|
||||
ACTION_MAKE_FRIEND = 0
|
||||
ACTION_INVITE = 1
|
||||
@ -34,25 +34,26 @@ class BuddyMenu(Menu):
|
||||
self._buddy = buddy
|
||||
self._shell = shell
|
||||
|
||||
Menu.__init__(self, buddy.get_name())
|
||||
pixbuf = self._get_buddy_icon_pixbuf()
|
||||
if pixbuf:
|
||||
icon_item = hippo.CanvasImage()
|
||||
scaled_pixbuf = pixbuf.scale_simple(_ICON_SIZE, _ICON_SIZE,
|
||||
scaled_pixbuf = pixbuf.scale_simple(units.grid_to_pixels(1),
|
||||
units.grid_to_pixels(1),
|
||||
gtk.gdk.INTERP_BILINEAR)
|
||||
del pixbuf
|
||||
Menu.__init__(self, buddy.get_name(), icon_item)
|
||||
self.add_separator()
|
||||
self.append(icon_item)
|
||||
# FIXME: have to set the image _after_ adding the HippoCanvasImage
|
||||
# to it's parent item, because that sets the HippoCanvasImage's context,
|
||||
# which resets the object's 'image' property. Grr.
|
||||
_sugar.hippo_canvas_image_set_image_from_gdk_pixbuf(icon_item, scaled_pixbuf)
|
||||
else:
|
||||
Menu.__init__(self, buddy.get_name(), None)
|
||||
|
||||
self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb)
|
||||
|
||||
owner = shell.get_model().get_owner()
|
||||
if buddy.get_name() != owner.get_name():
|
||||
self._add_actions()
|
||||
self._add_items()
|
||||
|
||||
def _get_buddy_icon_pixbuf(self):
|
||||
buddy_object = self._buddy.get_buddy()
|
||||
@ -76,17 +77,19 @@ class BuddyMenu(Menu):
|
||||
del pbl
|
||||
return pixbuf
|
||||
|
||||
def _add_actions(self):
|
||||
def _add_items(self):
|
||||
shell_model = self._shell.get_model()
|
||||
pservice = PresenceService.get_instance()
|
||||
|
||||
friends = shell_model.get_friends()
|
||||
if friends.has_buddy(self._buddy):
|
||||
icon = CanvasIcon(icon_name='theme:stock-remove')
|
||||
self.add_action(icon, BuddyMenu.ACTION_REMOVE_FRIEND)
|
||||
self.add_item(MenuItem(BuddyMenu.ACTION_REMOVE_FRIEND,
|
||||
_('Remove friend'),
|
||||
'theme:stock-remove'))
|
||||
else:
|
||||
icon = CanvasIcon(icon_name='theme:stock-add')
|
||||
self.add_action(icon, BuddyMenu.ACTION_MAKE_FRIEND)
|
||||
self.add_item(MenuItem(BuddyMenu.ACTION_MAKE_FRIEND,
|
||||
_('Make friend'),
|
||||
'theme:stock-add'))
|
||||
|
||||
activity = shell_model.get_home().get_current_activity()
|
||||
if activity != None:
|
||||
@ -94,9 +97,9 @@ class BuddyMenu(Menu):
|
||||
|
||||
# FIXME check that the buddy is not in the activity already
|
||||
|
||||
icon = CanvasIcon(icon_name='theme:stock-invite')
|
||||
self.add_action(icon, BuddyMenu.ACTION_INVITE)
|
||||
self.add_item(MenuItem(BuddyMenu.ACTION_INVITE,
|
||||
_('Invite'),
|
||||
'theme:stock-invite'))
|
||||
|
||||
def __buddy_icon_changed_cb(self, buddy):
|
||||
pass
|
||||
|
||||
|
@ -20,10 +20,10 @@ import gobject
|
||||
import wnck
|
||||
|
||||
from view.home.HomeWindow import HomeWindow
|
||||
from sugar.presence import PresenceService
|
||||
from sugar.activity.activityhandle import ActivityHandle
|
||||
from sugar.graphics.popupcontext import PopupContext
|
||||
from view.ActivityHost import ActivityHost
|
||||
from sugar.activity import ActivityFactory
|
||||
from sugar.activity import activityfactory
|
||||
from view.frame.frame import Frame
|
||||
from view.keyhandler import KeyHandler
|
||||
from view.hardwaremanager import HardwareManager
|
||||
@ -60,8 +60,6 @@ class Shell(gobject.GObject):
|
||||
self._frame = Frame(self)
|
||||
self._frame.show_and_hide(3)
|
||||
|
||||
self._pservice = PresenceService.get_instance()
|
||||
|
||||
self.start_activity('org.laptop.JournalActivity')
|
||||
|
||||
def _activity_added_cb(self, home_model, home_activity):
|
||||
@ -105,13 +103,8 @@ class Shell(gobject.GObject):
|
||||
def get_popup_context(self):
|
||||
return self._popup_context
|
||||
|
||||
def _join_success_cb(self, handler, activity, activity_ps, activity_id, activity_type):
|
||||
logging.debug("Joining activity %s (%s)" % (activity_id, activity_type))
|
||||
activity.join(activity_ps.object_path())
|
||||
|
||||
def _join_error_cb(self, handler, err, home_model, activity_id, activity_type):
|
||||
logging.error("Couldn't launch activity %s (%s):\n%s" % (activity_id, activity_type, err))
|
||||
home_mode.notify_activity_launch_failed(activity_id)
|
||||
def _join_error_cb(self, handler, err, home_model):
|
||||
home_mode.notify_activity_launch_failed(handler.get_activity_id())
|
||||
|
||||
def join_activity(self, bundle_id, activity_id):
|
||||
activity = self.get_activity(activity_id)
|
||||
@ -119,11 +112,6 @@ class Shell(gobject.GObject):
|
||||
activity.present()
|
||||
return
|
||||
|
||||
activity_ps = self._pservice.get_activity(activity_id)
|
||||
if not activity_ps:
|
||||
logging.error("Couldn't find shared activity for %s" % activity_id)
|
||||
return
|
||||
|
||||
# Get the service name for this activity, if
|
||||
# we have a bundle on the system capable of handling
|
||||
# this activity type
|
||||
@ -137,62 +125,25 @@ class Shell(gobject.GObject):
|
||||
home_model = self._model.get_home()
|
||||
home_model.notify_activity_launch(activity_id, act_type)
|
||||
|
||||
handler = ActivityFactory.create(act_type)
|
||||
handler.connect('success', self._join_success_cb, activity_ps, activity_id, act_type)
|
||||
handler.connect('error', self._join_error_cb, home_model, activity_id, act_type)
|
||||
handle = ActivityHandle(activity_id)
|
||||
handle.pservice_id = activity_id
|
||||
|
||||
def _find_unique_activity_id(self):
|
||||
# create a new unique activity ID
|
||||
i = 0
|
||||
act_id = None
|
||||
while i < 10:
|
||||
act_id = sugar.util.unique_id()
|
||||
i += 1
|
||||
handler = activityfactory.create(act_type, handle)
|
||||
handler.connect('error', self._join_error_cb, home_model)
|
||||
|
||||
# check through existing activities
|
||||
found = False
|
||||
for xid, act_host in self._hosts.items():
|
||||
if act_host.get_id() == act_id:
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
act_id = None
|
||||
continue
|
||||
|
||||
# check through network activities
|
||||
activities = self._pservice.get_activities()
|
||||
for act in activities:
|
||||
if act_id == act.get_id():
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
act_id = None
|
||||
continue
|
||||
|
||||
return act_id
|
||||
|
||||
def _start_success_cb(self, handler, activity, activity_id, activity_type):
|
||||
logging.debug("Started activity %s (%s)" % (activity_id, activity_type))
|
||||
activity.start(activity_id)
|
||||
|
||||
def _start_error_cb(self, handler, err, home_model, activity_id, activity_type):
|
||||
logging.error("Couldn't launch activity %s (%s):\n%s" % (activity_id, activity_type, err))
|
||||
home_model.notify_activity_launch_failed(activity_id)
|
||||
def _start_error_cb(self, handler, err, home_model):
|
||||
home_model.notify_activity_launch_failed(handler.get_activity_id())
|
||||
|
||||
def start_activity(self, activity_type):
|
||||
logging.debug('Shell.start_activity')
|
||||
act_id = self._find_unique_activity_id()
|
||||
if not act_id:
|
||||
logging.error("Couldn't find available activity ID.")
|
||||
return None
|
||||
|
||||
handler = activityfactory.create(activity_type)
|
||||
|
||||
home_model = self._model.get_home()
|
||||
home_model.notify_activity_launch(act_id, activity_type)
|
||||
home_model.notify_activity_launch(handler.get_activity_id(),
|
||||
activity_type)
|
||||
|
||||
logging.debug("Shell.start_activity will start %s (%s)" % (act_id, activity_type))
|
||||
handler = ActivityFactory.create(activity_type)
|
||||
handler.connect('success', self._start_success_cb, act_id, activity_type)
|
||||
handler.connect('error', self._start_error_cb, home_model, act_id, activity_type)
|
||||
handler.connect('error', self._start_error_cb, home_model)
|
||||
|
||||
# Zoom to Home for launch feedback
|
||||
self.set_zoom_level(sugar.ZOOM_HOME)
|
||||
|
@ -1,16 +1,17 @@
|
||||
import logging
|
||||
|
||||
from sugar.graphics.menuicon import MenuIcon
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from view.clipboardmenu import ClipboardMenu
|
||||
from sugar.graphics.iconcolor import IconColor
|
||||
from sugar.activity import ActivityFactory
|
||||
from sugar.activity import activityfactory
|
||||
from sugar.clipboard import clipboardservice
|
||||
from sugar import util
|
||||
|
||||
class ClipboardIcon(MenuIcon):
|
||||
class ClipboardIcon(CanvasIcon):
|
||||
|
||||
def __init__(self, menu_shell, object_id, name):
|
||||
MenuIcon.__init__(self, menu_shell)
|
||||
def __init__(self, popup_context, object_id, name):
|
||||
CanvasIcon.__init__(self)
|
||||
self._popup_context = popup_context
|
||||
self._object_id = object_id
|
||||
self._name = name
|
||||
self._percent = 0
|
||||
@ -19,12 +20,15 @@ class ClipboardIcon(MenuIcon):
|
||||
self.connect('activated', self._icon_activated_cb)
|
||||
self._menu = None
|
||||
|
||||
def create_menu(self):
|
||||
def get_popup(self):
|
||||
self._menu = ClipboardMenu(self._name, self._percent, self._preview,
|
||||
self._activity)
|
||||
self._menu.connect('action', self._popup_action_cb)
|
||||
return self._menu
|
||||
|
||||
def get_popup_context(self):
|
||||
return self._popup_context
|
||||
|
||||
def set_state(self, name, percent, icon_name, preview, activity):
|
||||
self._name = name
|
||||
self._percent = percent
|
||||
@ -53,15 +57,15 @@ class ClipboardIcon(MenuIcon):
|
||||
logging.debug("_icon_activated_cb: " + self._object_id)
|
||||
|
||||
# Launch the activity to handle this item
|
||||
handler = ActivityFactory.create(self._activity)
|
||||
handler = activityfactory.create(self._activity)
|
||||
handler.connect('success', self._activity_create_success_cb)
|
||||
handler.connect('error', self._activity_create_error_cb)
|
||||
|
||||
def _icon_activated_cb(self, icon):
|
||||
self._open_file()
|
||||
|
||||
def _popup_action_cb(self, popup, action):
|
||||
self.popdown()
|
||||
def _popup_action_cb(self, popup, menu_item):
|
||||
action = menu_item.props.action_id
|
||||
|
||||
if action == ClipboardMenu.ACTION_STOP_DOWNLOAD:
|
||||
raise "Stopping downloads still not implemented."
|
||||
|
@ -1,16 +1,23 @@
|
||||
from gettext import gettext as _
|
||||
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.menu import Menu
|
||||
from sugar.graphics.menu import Menu, MenuItem
|
||||
from sugar.graphics.canvasicon import CanvasIcon
|
||||
from sugar.graphics.ClipboardBubble import ClipboardBubble
|
||||
from sugar.graphics import color
|
||||
from sugar.graphics import font
|
||||
|
||||
class ClipboardMenuItem(ClipboardBubble):
|
||||
class ClipboardProgressBar(ClipboardBubble):
|
||||
|
||||
def __init__(self, percent = 0):
|
||||
self._text_item = None
|
||||
ClipboardBubble.__init__(self, percent=percent)
|
||||
|
||||
self._text_item = hippo.CanvasText(text=str(percent) + ' %')
|
||||
self._text_item.props.color = color.LABEL_TEXT.get_int()
|
||||
self._text_item.props.font_desc = font.DEFAULT.get_pango_desc()
|
||||
|
||||
self.append(self._text_item)
|
||||
|
||||
def do_set_property(self, pspec, value):
|
||||
@ -30,57 +37,69 @@ class ClipboardMenu(Menu):
|
||||
Menu.__init__(self, name)
|
||||
|
||||
if percent < 100:
|
||||
self._progress_bar = ClipboardMenuItem(percent)
|
||||
self._root.append(self._progress_bar)
|
||||
self._progress_bar = ClipboardProgressBar(percent)
|
||||
self.append(self._progress_bar)
|
||||
else:
|
||||
self._progress_bar = None
|
||||
|
||||
self._remove_icon = None
|
||||
self._open_icon = None
|
||||
self._stop_icon = None
|
||||
|
||||
self.add_item(preview, wrap=True)
|
||||
self._remove_item = None
|
||||
self._open_item = None
|
||||
self._stop_item = None
|
||||
|
||||
if preview:
|
||||
self._preview_text = hippo.CanvasText(text=preview,
|
||||
size_mode=hippo.CANVAS_SIZE_WRAP_WORD)
|
||||
self._preview_text.props.color = color.LABEL_TEXT.get_int()
|
||||
self._preview_text.props.font_desc = font.DEFAULT.get_pango_desc()
|
||||
self.append(self._preview_text)
|
||||
|
||||
self._update_icons(percent, activity)
|
||||
|
||||
def _update_icons(self, percent, activity):
|
||||
|
||||
if percent == 100 and activity:
|
||||
if not self._remove_icon:
|
||||
self._remove_icon = CanvasIcon(icon_name='theme:stock-remove')
|
||||
self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE)
|
||||
if not self._remove_item:
|
||||
self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE,
|
||||
_('Remove'),
|
||||
'theme:stock-remove')
|
||||
self.add_item(self._remove_item)
|
||||
|
||||
if not self._open_icon:
|
||||
self._open_icon = CanvasIcon(icon_name='theme:stock-keep')
|
||||
self.add_action(self._open_icon, ClipboardMenu.ACTION_OPEN)
|
||||
if not self._open_item:
|
||||
self._open_item = MenuItem(ClipboardMenu.ACTION_OPEN,
|
||||
_('Open'),
|
||||
'theme:stock-keep')
|
||||
self.add_item(self._open_item)
|
||||
|
||||
if self._stop_icon:
|
||||
self.remove_action(self._stop_icon)
|
||||
self._stop_icon = None
|
||||
if self._stop_item:
|
||||
self.remove_item(self._stop_item)
|
||||
self._stop_item = None
|
||||
elif percent == 100 and not activity:
|
||||
if not self._remove_icon:
|
||||
self._remove_icon = CanvasIcon(icon_name='theme:stock-remove')
|
||||
self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE)
|
||||
if not self._remove_item:
|
||||
self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE,
|
||||
_('Remove'),
|
||||
'theme:stock-remove')
|
||||
self.add_item(self._remove_item)
|
||||
|
||||
if self._open_icon:
|
||||
self.remove_action(self._open_icon)
|
||||
self._open_icon = None
|
||||
if self._open_item:
|
||||
self.remove_item(self._open_item)
|
||||
self._open_item = None
|
||||
|
||||
if self._stop_icon:
|
||||
self.remove_action(self._stop_icon)
|
||||
self._stop_icon = None
|
||||
if self._stop_item:
|
||||
self.remove_item(self._stop_item)
|
||||
self._stop_item = None
|
||||
else:
|
||||
if not self._stop_icon:
|
||||
self._stop_icon = CanvasIcon(icon_name='theme:stock-close')
|
||||
self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD)
|
||||
if not self._stop_item:
|
||||
self._stop_item = MenuItem(ClipboardMenu.ACTION_STOP_DOWNLOAD,
|
||||
_('Stop download'),
|
||||
'theme:stock-close')
|
||||
self.add_item(self._stop_item)
|
||||
|
||||
if self._remove_icon:
|
||||
self.remove_action(self._remove_icon)
|
||||
self._remove_icon = None
|
||||
if self._remove_item:
|
||||
self.remove_item(self._remove_item)
|
||||
self._remove_item = None
|
||||
|
||||
if self._open_icon:
|
||||
self.remove_action(self._open_icon)
|
||||
self._open_icon = None
|
||||
if self._open_item:
|
||||
self.remove_item(self._open_item)
|
||||
self._open_item = None
|
||||
|
||||
def set_state(self, name, percent, preview, activity):
|
||||
self.set_title(name)
|
||||
|
@ -21,6 +21,7 @@ from sugar.graphics import units
|
||||
from sugar.graphics.iconcolor import IconColor
|
||||
from sugar.graphics.iconbutton import IconButton
|
||||
from sugar.presence import PresenceService
|
||||
from sugar.activity import bundleregistry
|
||||
from sugar import profile
|
||||
|
||||
class ActivityButton(IconButton):
|
||||
@ -63,7 +64,7 @@ class ActivitiesBox(hippo.CanvasBox):
|
||||
self._invite_to_item = {}
|
||||
self._invites = self._shell_model.get_invites()
|
||||
|
||||
bundle_registry = self._shell_model.get_bundle_registry()
|
||||
bundle_registry = bundleregistry.get_registry()
|
||||
for bundle in bundle_registry:
|
||||
if bundle.get_show_launcher():
|
||||
self.add_activity(bundle)
|
||||
|
@ -10,4 +10,5 @@ sugar_PYTHON = \
|
||||
ZoomBox.py \
|
||||
notificationtray.py \
|
||||
overlaybox.py \
|
||||
PanelWindow.py
|
||||
PanelWindow.py \
|
||||
framepopupcontext.py
|
||||
|
@ -17,7 +17,6 @@
|
||||
import gtk
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.menushell import MenuShell
|
||||
from sugar.graphics import units
|
||||
|
||||
class PanelWindow(gtk.Window):
|
||||
@ -51,13 +50,8 @@ class PanelWindow(gtk.Window):
|
||||
self.add(self._canvas)
|
||||
self._canvas.show()
|
||||
|
||||
self._menu_shell = MenuShell(self._canvas)
|
||||
|
||||
self.resize(width, height)
|
||||
|
||||
def get_menu_shell(self):
|
||||
return self._menu_shell
|
||||
|
||||
def get_root(self):
|
||||
return self._bg
|
||||
|
||||
|
@ -19,31 +19,32 @@ from gettext import gettext as _
|
||||
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.popup import Popup
|
||||
from sugar.graphics.menuicon import MenuIcon
|
||||
from sugar.graphics.menu import Menu
|
||||
from sugar.graphics.menu import Menu, MenuItem
|
||||
from sugar.graphics.iconcolor import IconColor
|
||||
from sugar.graphics.iconbutton import IconButton
|
||||
import sugar
|
||||
|
||||
class ActivityPopup(Popup):
|
||||
class ActivityMenu(Menu):
|
||||
ACTION_SHARE = 1
|
||||
ACTION_CLOSE = 2
|
||||
|
||||
def __init__(self, activity_model):
|
||||
Popup.__init__(self, activity_model.get_title())
|
||||
Menu.__init__(self, activity_model.get_title())
|
||||
|
||||
if not activity_model.get_shared():
|
||||
self.add_item(ActivityPopup.ACTION_SHARE, _('Share'),
|
||||
'theme:stock-share-mesh')
|
||||
self.add_item(MenuItem(ActivityMenu.ACTION_SHARE,
|
||||
_('Share'),
|
||||
'theme:stock-share-mesh'))
|
||||
|
||||
self.add_item(ActivityPopup.ACTION_CLOSE, _('Close'),
|
||||
'theme:stock-close')
|
||||
self.add_item(MenuItem(ActivityMenu.ACTION_CLOSE,
|
||||
_('Close'),
|
||||
'theme:stock-close'))
|
||||
|
||||
class ActivityButton(IconButton):
|
||||
def __init__(self, shell, activity_model):
|
||||
def __init__(self, shell, activity_model, popup_context):
|
||||
self._shell = shell
|
||||
self._activity_model = activity_model
|
||||
self._popup_context = popup_context
|
||||
|
||||
icon_name = self._activity_model.get_icon_name()
|
||||
icon_color = self._activity_model.get_icon_color()
|
||||
@ -51,16 +52,14 @@ class ActivityButton(IconButton):
|
||||
IconButton.__init__(self, icon_name=icon_name, color=icon_color)
|
||||
|
||||
def get_popup(self):
|
||||
popup = ActivityPopup(self._activity_model)
|
||||
#popup.connect('action', self._action_cb)
|
||||
return popup
|
||||
menu = ActivityMenu(self._activity_model)
|
||||
menu.connect('action', self._action_cb)
|
||||
return menu
|
||||
|
||||
def get_popup_context(self):
|
||||
return self._shell.get_popup_context()
|
||||
return self._popup_context
|
||||
|
||||
def _action_cb(self, menu, data):
|
||||
[action_id, label] = data
|
||||
|
||||
def _action_cb(self, menu, menu_item):
|
||||
# TODO: Wouldn't be better to share/close the activity associated with
|
||||
# this button instead of asking for the current activity?
|
||||
activity = self._shell.get_current_activity()
|
||||
@ -68,17 +67,17 @@ class ActivityButton(IconButton):
|
||||
logging.error('No active activity.')
|
||||
return
|
||||
|
||||
if action_id == ActivityPopup.ACTION_SHARE:
|
||||
if menu_item.props.action_id == ActivityMenu.ACTION_SHARE:
|
||||
activity.share()
|
||||
elif action_id == ActivityPopup.ACTION_CLOSE:
|
||||
elif menu_item.props.action_id == ActivityMenu.ACTION_CLOSE:
|
||||
activity.close()
|
||||
|
||||
class ZoomBox(hippo.CanvasBox):
|
||||
def __init__(self, shell, menu_shell):
|
||||
def __init__(self, shell, popup_context):
|
||||
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
|
||||
|
||||
self._shell = shell
|
||||
self._menu_shell = menu_shell
|
||||
self._popup_context = popup_context
|
||||
self._activity_icon = None
|
||||
|
||||
icon = IconButton(icon_name='theme:stock-zoom-mesh')
|
||||
@ -107,7 +106,7 @@ class ZoomBox(hippo.CanvasBox):
|
||||
self.remove(self._activity_icon)
|
||||
|
||||
if home_activity:
|
||||
icon = ActivityButton(self._shell, home_activity)
|
||||
icon = ActivityButton(self._shell, home_activity, self._popup_context)
|
||||
self.append(icon)
|
||||
self._activity_icon = icon
|
||||
else:
|
||||
|
@ -37,9 +37,9 @@ class _ContextMap:
|
||||
|
||||
class ClipboardBox(hippo.CanvasBox):
|
||||
|
||||
def __init__(self, menu_shell):
|
||||
def __init__(self, popup_context):
|
||||
hippo.CanvasBox.__init__(self)
|
||||
self._menu_shell = menu_shell
|
||||
self._popup_context = popup_context
|
||||
self._icons = {}
|
||||
self._context_map = _ContextMap()
|
||||
|
||||
@ -74,7 +74,7 @@ class ClipboardBox(hippo.CanvasBox):
|
||||
on_disk = False)
|
||||
|
||||
def _object_added_cb(self, cb_service, object_id, name):
|
||||
icon = ClipboardIcon(self._menu_shell, object_id, name)
|
||||
icon = ClipboardIcon(self._popup_context, object_id, name)
|
||||
self.append(icon)
|
||||
self._icons[object_id] = icon
|
||||
|
||||
|
@ -17,10 +17,9 @@ class ClipboardPanelWindow(PanelWindow):
|
||||
clipboard = gtk.Clipboard()
|
||||
clipboard.connect("owner-change", self._owner_change_cb)
|
||||
|
||||
menu_shell = self.get_menu_shell()
|
||||
root = self.get_root()
|
||||
|
||||
box = ClipboardBox(menu_shell)
|
||||
box = ClipboardBox(frame.get_popup_context())
|
||||
root.append(box)
|
||||
|
||||
# Receiving dnd drops
|
||||
|
@ -27,9 +27,9 @@ from view.frame.FriendsBox import FriendsBox
|
||||
from view.frame.PanelWindow import PanelWindow
|
||||
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
||||
from view.frame.notificationtray import NotificationTray
|
||||
from view.frame.framepopupcontext import FramePopupContext
|
||||
from model.ShellModel import ShellModel
|
||||
from sugar.graphics.timeline import Timeline
|
||||
from sugar.graphics.menushell import MenuShell
|
||||
from sugar.graphics import units
|
||||
|
||||
_ANIMATION = False
|
||||
@ -62,6 +62,12 @@ class Frame:
|
||||
self._event_frame.connect('leave', self._event_frame_leave_cb)
|
||||
self._event_frame.show()
|
||||
|
||||
self._popup_context = FramePopupContext()
|
||||
self._popup_context.connect('activated',
|
||||
self._popup_context_activated_cb)
|
||||
self._popup_context.connect('deactivated',
|
||||
self._popup_context_deactivated_cb)
|
||||
|
||||
self._top_panel = self._create_top_panel()
|
||||
self._bottom_panel = self._create_bottom_panel()
|
||||
self._left_panel = self._create_left_panel()
|
||||
@ -70,20 +76,11 @@ class Frame:
|
||||
shell.get_model().connect('notify::state',
|
||||
self._shell_state_changed_cb)
|
||||
|
||||
popup_context = shell.get_popup_context()
|
||||
popup_context.connect('activated',
|
||||
self._popup_context_activated_cb)
|
||||
popup_context.connect('deactivated',
|
||||
self._popup_context_deactivated_cb)
|
||||
|
||||
def _create_top_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||
menu_shell = panel.get_menu_shell()
|
||||
root = panel.get_root()
|
||||
|
||||
menu_shell.set_position(MenuShell.BOTTOM)
|
||||
|
||||
box = ZoomBox(self._shell, menu_shell)
|
||||
box = ZoomBox(self._shell, self._popup_context)
|
||||
root.append(box)
|
||||
|
||||
tray = NotificationTray()
|
||||
@ -103,11 +100,8 @@ class Frame:
|
||||
|
||||
def _create_bottom_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||
menu_shell = panel.get_menu_shell()
|
||||
root = panel.get_root()
|
||||
|
||||
menu_shell.set_position(MenuShell.TOP)
|
||||
|
||||
box = ActivitiesBox(self._shell)
|
||||
root.append(box)
|
||||
|
||||
@ -115,12 +109,9 @@ class Frame:
|
||||
|
||||
def _create_right_panel(self):
|
||||
panel = self._create_panel(hippo.ORIENTATION_VERTICAL)
|
||||
menu_shell = panel.get_menu_shell()
|
||||
root = panel.get_root()
|
||||
|
||||
menu_shell.set_position(MenuShell.LEFT)
|
||||
|
||||
box = FriendsBox(self._shell, menu_shell)
|
||||
box = FriendsBox(self._shell, self._popup_context)
|
||||
root.append(box)
|
||||
|
||||
return panel
|
||||
@ -158,19 +149,6 @@ class Frame:
|
||||
panel.connect('enter-notify-event', self._enter_notify_cb)
|
||||
panel.connect('leave-notify-event', self._leave_notify_cb)
|
||||
|
||||
menu_shell = panel.get_menu_shell()
|
||||
menu_shell.connect('activated',
|
||||
self._menu_shell_activated_cb)
|
||||
menu_shell.connect('deactivated',
|
||||
self._menu_shell_deactivated_cb)
|
||||
|
||||
def _menu_shell_activated_cb(self, menu_shell):
|
||||
self._timeline.goto('slide_in', True)
|
||||
|
||||
def _menu_shell_deactivated_cb(self, menu_shell):
|
||||
if self._mode != Frame.STICKY and not self._hover_frame:
|
||||
self._timeline.play('before_slide_out', 'slide_out')
|
||||
|
||||
def _popup_context_activated_cb(self, popup_context):
|
||||
self._timeline.goto('slide_in', True)
|
||||
|
||||
@ -205,8 +183,7 @@ class Frame:
|
||||
|
||||
def _leave_notify(self, panel):
|
||||
self._hover_frame = False
|
||||
if not panel.get_menu_shell().is_active() and \
|
||||
not self._shell.get_popup_context().is_active() and \
|
||||
if not self._popup_context.is_active() and \
|
||||
(self._mode == Frame.HIDE_ON_LEAVE or \
|
||||
self._mode == Frame.AUTOMATIC):
|
||||
self._timeline.play('before_slide_out', 'slide_out')
|
||||
@ -281,3 +258,6 @@ class Frame:
|
||||
|
||||
def is_visible(self):
|
||||
return self._top_panel.props.visible
|
||||
|
||||
def get_popup_context(self):
|
||||
return self._popup_context
|
||||
|
26
shell/view/frame/framepopupcontext.py
Normal file
26
shell/view/frame/framepopupcontext.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Copyright (C) 2007, One Laptop Per Child
|
||||
#
|
||||
# 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 gobject
|
||||
import hippo
|
||||
|
||||
from sugar.graphics.popupcontext import PopupContext
|
||||
|
||||
class FramePopupContext(PopupContext):
|
||||
__gtype_name__ = 'SugarFramePopupContext'
|
||||
|
||||
def __init__(self):
|
||||
PopupContext.__init__(self)
|
@ -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})
|
@ -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)
|
@ -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
sugar/activity/activity.py
Normal file
108
sugar/activity/activity.py
Normal 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
sugar/activity/activityfactory.py
Normal file
102
sugar/activity/activityfactory.py
Normal 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
sugar/activity/activityfactoryservice.py
Normal file
97
sugar/activity/activityfactoryservice.py
Normal 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
sugar/activity/activityhandle.py
Normal file
49
sugar/activity/activityhandle.py
Normal 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
sugar/activity/activityservice.py
Normal file
68
sugar/activity/activityservice.py
Normal 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)
|
||||
|
@ -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
|
||||
|
@ -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
sugar/env.py
18
sugar/env.py
@ -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']
|
||||
|
@ -12,7 +12,6 @@ sugar_PYTHON = \
|
||||
iconcolor.py \
|
||||
label.py \
|
||||
menu.py \
|
||||
menuicon.py \
|
||||
menushell.py \
|
||||
optionmenu.py \
|
||||
roundbox.py \
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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')
|
@ -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()
|
||||
|
@ -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')
|
||||
|
@ -24,6 +24,7 @@ import hippo
|
||||
|
||||
from sugar.graphics.toolbar import Toolbar
|
||||
from sugar.graphics.optionmenu import OptionMenu
|
||||
from sugar.graphics.menu import MenuItem
|
||||
from sugar.graphics.iconbutton import IconButton
|
||||
|
||||
def _option_menu_changed_cb(option_menu):
|
||||
@ -56,11 +57,11 @@ OPTION_WRITE = 3
|
||||
OPTION_CHAT = 4
|
||||
|
||||
option_menu = OptionMenu()
|
||||
option_menu.add_option(OPTION_ANYTHING, _('Anything'))
|
||||
option_menu.add_item(MenuItem(OPTION_ANYTHING, _('Anything')))
|
||||
option_menu.add_separator()
|
||||
option_menu.add_option(OPTION_DRAW, _('Draw'), 'theme:stock-close')
|
||||
option_menu.add_option(OPTION_WRITE, _('Write'))
|
||||
option_menu.add_option(OPTION_CHAT, _('Chat'))
|
||||
option_menu.add_item(MenuItem(OPTION_DRAW, _('Draw'), 'theme:stock-close'))
|
||||
option_menu.add_item(MenuItem(OPTION_WRITE, _('Write')))
|
||||
option_menu.add_item(MenuItem(OPTION_CHAT, _('Chat')))
|
||||
option_menu.connect('changed', _option_menu_changed_cb)
|
||||
toolbar.append(option_menu)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user