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.
|
* Boston, MA 02111-1307, USA.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <math.h>
|
#include <stdlib.h>
|
||||||
#include <gdk/gdkx.h>
|
#include <gdk/gdkx.h>
|
||||||
|
|
||||||
|
#include "sugar-utils.h"
|
||||||
|
|
||||||
gint
|
gint
|
||||||
sugar_get_screen_dpi(void)
|
sugar_get_screen_dpi(void)
|
||||||
{
|
{
|
||||||
@ -27,8 +29,8 @@ sugar_get_screen_dpi(void)
|
|||||||
if (val) {
|
if (val) {
|
||||||
char *e;
|
char *e;
|
||||||
double d = strtod(val, &e);
|
double d = strtod(val, &e);
|
||||||
if (e != val)
|
if (d > 0.0)
|
||||||
return round(d);
|
return (int)(d+0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 96;
|
return 96;
|
||||||
|
@ -29,7 +29,7 @@ class TextFileType(FileType):
|
|||||||
return _('Text snippet')
|
return _('Text snippet')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-xbook'
|
return 'theme:object-text'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
for format, data in self._formats.iteritems():
|
for format, data in self._formats.iteritems():
|
||||||
@ -57,7 +57,7 @@ class ImageFileType(FileType):
|
|||||||
return _('Image')
|
return _('Image')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-sketch'
|
return 'theme:object-image'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
return ''
|
return ''
|
||||||
@ -77,7 +77,7 @@ class UriFileType(FileType):
|
|||||||
return _('Web Page')
|
return _('Web Page')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-web'
|
return 'theme:object-link'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
for format, data in self._formats.iteritems():
|
for format, data in self._formats.iteritems():
|
||||||
@ -103,7 +103,7 @@ class PdfFileType(FileType):
|
|||||||
return _('PDF file')
|
return _('PDF file')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-xbook'
|
return 'theme:object-text'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
return ''
|
return ''
|
||||||
@ -123,7 +123,7 @@ class MsWordFileType(FileType):
|
|||||||
return _('MS Word file')
|
return _('MS Word file')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-abiword'
|
return 'theme:object-text'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
return ''
|
return ''
|
||||||
@ -143,7 +143,7 @@ class RtfFileType(FileType):
|
|||||||
return _('RTF file')
|
return _('RTF file')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-abiword'
|
return 'theme:object-text'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
return ''
|
return ''
|
||||||
@ -163,7 +163,7 @@ class OOTextFileType(FileType):
|
|||||||
return _('OpenOffice text file')
|
return _('OpenOffice text file')
|
||||||
|
|
||||||
def get_icon(self):
|
def get_icon(self):
|
||||||
return 'theme:activity-abiword'
|
return 'theme:object-text'
|
||||||
|
|
||||||
def get_preview(self):
|
def get_preview(self):
|
||||||
return ''
|
return ''
|
||||||
|
@ -18,6 +18,7 @@ import gobject
|
|||||||
|
|
||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
from sugar.presence import PresenceService
|
from sugar.presence import PresenceService
|
||||||
|
from sugar.activity import bundleregistry
|
||||||
from model.BuddyModel import BuddyModel
|
from model.BuddyModel import BuddyModel
|
||||||
|
|
||||||
class ActivityModel:
|
class ActivityModel:
|
||||||
@ -53,12 +54,12 @@ class MeshModel(gobject.GObject):
|
|||||||
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
|
gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT]))
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, bundle_registry):
|
def __init__(self):
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
self._activities = {}
|
self._activities = {}
|
||||||
self._buddies = {}
|
self._buddies = {}
|
||||||
self._bundle_registry = bundle_registry
|
self._bundle_registry = bundleregistry.get_registry()
|
||||||
|
|
||||||
self._pservice = PresenceService.get_instance()
|
self._pservice = PresenceService.get_instance()
|
||||||
self._pservice.connect("service-appeared",
|
self._pservice.connect("service-appeared",
|
||||||
|
@ -44,8 +44,6 @@ class ShellModel(gobject.GObject):
|
|||||||
self._current_activity = None
|
self._current_activity = None
|
||||||
self._state = self.STATE_RUNNING
|
self._state = self.STATE_RUNNING
|
||||||
|
|
||||||
self._bundle_registry = BundleRegistry()
|
|
||||||
|
|
||||||
PresenceService.start()
|
PresenceService.start()
|
||||||
self._pservice = PresenceService.get_instance()
|
self._pservice = PresenceService.get_instance()
|
||||||
|
|
||||||
@ -53,16 +51,10 @@ class ShellModel(gobject.GObject):
|
|||||||
self._owner.announce()
|
self._owner.announce()
|
||||||
|
|
||||||
self._friends = Friends()
|
self._friends = Friends()
|
||||||
self._mesh = MeshModel(self._bundle_registry)
|
self._mesh = MeshModel()
|
||||||
self._home = HomeModel(self._bundle_registry)
|
self._home = HomeModel()
|
||||||
self._devices = DevicesModel()
|
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):
|
def do_set_property(self, pspec, value):
|
||||||
if pspec.name == 'state':
|
if pspec.name == 'state':
|
||||||
self._state = value
|
self._state = value
|
||||||
@ -71,9 +63,6 @@ class ShellModel(gobject.GObject):
|
|||||||
if pspec.name == 'state':
|
if pspec.name == 'state':
|
||||||
return self._state
|
return self._state
|
||||||
|
|
||||||
def get_bundle_registry(self):
|
|
||||||
return self._bundle_registry
|
|
||||||
|
|
||||||
def get_mesh(self):
|
def get_mesh(self):
|
||||||
return self._mesh
|
return self._mesh
|
||||||
|
|
||||||
|
@ -15,14 +15,18 @@
|
|||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
|
||||||
import time
|
import time
|
||||||
import gobject
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import gobject
|
||||||
|
import dbus
|
||||||
|
|
||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
from sugar.presence import PresenceService
|
from sugar.presence import PresenceService
|
||||||
from sugar.activity import Activity
|
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
|
|
||||||
|
_ACTIVITY_SERVICE_NAME = "org.laptop.Activity"
|
||||||
|
_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity"
|
||||||
|
|
||||||
class HomeActivity(gobject.GObject):
|
class HomeActivity(gobject.GObject):
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'launch-timeout': (gobject.SIGNAL_RUN_FIRST,
|
'launch-timeout': (gobject.SIGNAL_RUN_FIRST,
|
||||||
@ -69,16 +73,22 @@ class HomeActivity(gobject.GObject):
|
|||||||
|
|
||||||
self._window = window
|
self._window = window
|
||||||
self._xid = window.get_xid()
|
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
|
# verify id and type details
|
||||||
act_id = self._service.get_id()
|
act_id = self._service.get_id()
|
||||||
if act_id != self._id:
|
if act_id != self._id:
|
||||||
raise RuntimeError("Activity's real ID (%s) didn't match expected (%s)." % (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:
|
if act_type != self._type:
|
||||||
raise RuntimeError("Activity's real type (%s) didn't match expected (%s)." % (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):
|
def get_title(self):
|
||||||
if not self._launched:
|
if not self._launched:
|
||||||
raise RuntimeError("Activity is still launching.")
|
raise RuntimeError("Activity is still launching.")
|
||||||
|
@ -18,9 +18,13 @@ import logging
|
|||||||
|
|
||||||
import gobject
|
import gobject
|
||||||
import wnck
|
import wnck
|
||||||
|
import dbus
|
||||||
|
|
||||||
from model.homeactivity import HomeActivity
|
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):
|
class HomeModel(gobject.GObject):
|
||||||
|
|
||||||
@ -39,11 +43,11 @@ class HomeModel(gobject.GObject):
|
|||||||
([gobject.TYPE_PYOBJECT]))
|
([gobject.TYPE_PYOBJECT]))
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, bundle_registry):
|
def __init__(self):
|
||||||
gobject.GObject.__init__(self)
|
gobject.GObject.__init__(self)
|
||||||
|
|
||||||
self._activities = {}
|
self._activities = {}
|
||||||
self._bundle_registry = bundle_registry
|
self._bundle_registry = bundleregistry.get_registry()
|
||||||
self._current_activity = None
|
self._current_activity = None
|
||||||
|
|
||||||
screen = wnck.screen_get_default()
|
screen = wnck.screen_get_default()
|
||||||
@ -114,7 +118,10 @@ class HomeModel(gobject.GObject):
|
|||||||
self.emit('active-activity-changed', self._current_activity)
|
self.emit('active-activity-changed', self._current_activity)
|
||||||
|
|
||||||
def _add_activity(self, window):
|
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()
|
act_id = act_service.get_id()
|
||||||
|
|
||||||
activity = None
|
activity = None
|
||||||
@ -123,7 +130,7 @@ class HomeModel(gobject.GObject):
|
|||||||
else:
|
else:
|
||||||
# activity got lost, took longer to launch than we allow,
|
# activity got lost, took longer to launch than we allow,
|
||||||
# or it was launched by something other than the shell
|
# 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)
|
bundle = self._bundle_registry.get_bundle(act_type)
|
||||||
if not bundle:
|
if not bundle:
|
||||||
raise RuntimeError("No bundle for activity type '%s'." % act_type)
|
raise RuntimeError("No bundle for activity type '%s'." % act_type)
|
||||||
|
@ -20,7 +20,7 @@ import sys
|
|||||||
import os
|
import os
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
from sugar.activity import ActivityFactory
|
from sugar.activity import activityfactory
|
||||||
from sugar import env
|
from sugar import env
|
||||||
from sugar import util
|
from sugar import util
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_name
|
|||||||
|
|
||||||
loop = gobject.MainLoop()
|
loop = gobject.MainLoop()
|
||||||
|
|
||||||
handler = ActivityFactory.create(sys.argv[1])
|
handler = activityfactory.create(sys.argv[1])
|
||||||
handler.connect('success', _success_cb, loop)
|
handler.connect('success', _success_cb, loop)
|
||||||
handler.connect('error', _error_cb, loop)
|
handler.connect('error', _error_cb, loop)
|
||||||
|
|
||||||
|
@ -18,21 +18,6 @@
|
|||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import pygtk
|
from sugar.activity import activityfactoryservice
|
||||||
pygtk.require('2.0')
|
|
||||||
import gobject
|
|
||||||
import gtk
|
|
||||||
|
|
||||||
import dbus.glib
|
activityfactoryservice.run(sys.argv)
|
||||||
|
|
||||||
# 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()
|
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import gtk
|
import gtk
|
||||||
import dbus
|
import dbus
|
||||||
|
|
||||||
from sugar.activity import Activity
|
|
||||||
from sugar.p2p import Stream
|
from sugar.p2p import Stream
|
||||||
from sugar.p2p import network
|
from sugar.p2p import network
|
||||||
from sugar.chat import ActivityChat
|
from sugar.chat import ActivityChat
|
||||||
@ -42,7 +41,7 @@ class ActivityHost:
|
|||||||
self._model = model
|
self._model = model
|
||||||
self._id = model.get_id()
|
self._id = model.get_id()
|
||||||
self._window = model.get_window()
|
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())
|
self._gdk_window = gtk.gdk.window_foreign_new(self.get_xid())
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -14,12 +14,12 @@
|
|||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# 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
|
from view.BuddyMenu import BuddyMenu
|
||||||
|
|
||||||
class BuddyIcon(MenuIcon):
|
class BuddyIcon(CanvasIcon):
|
||||||
def __init__(self, shell, menu_shell, buddy):
|
def __init__(self, shell, menu_shell, buddy):
|
||||||
MenuIcon.__init__(self, menu_shell, icon_name='theme:stock-buddy',
|
CanvasIcon.__init__(self, icon_name='theme:stock-buddy',
|
||||||
color=buddy.get_color())
|
color=buddy.get_color())
|
||||||
|
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
@ -35,13 +35,16 @@ class BuddyIcon(MenuIcon):
|
|||||||
def set_popup_distance(self, distance):
|
def set_popup_distance(self, distance):
|
||||||
self._popup_distance = distance
|
self._popup_distance = distance
|
||||||
|
|
||||||
def create_menu(self):
|
def get_popup(self):
|
||||||
menu = BuddyMenu(self._shell, self._buddy)
|
menu = BuddyMenu(self._shell, self._buddy)
|
||||||
menu.connect('action', self._popup_action_cb)
|
menu.connect('action', self._popup_action_cb)
|
||||||
return menu
|
return menu
|
||||||
|
|
||||||
def _popup_action_cb(self, popup, action):
|
def get_popup_context(self):
|
||||||
self.popdown()
|
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()
|
friends = self._shell.get_model().get_friends()
|
||||||
if action == BuddyMenu.ACTION_REMOVE_FRIEND:
|
if action == BuddyMenu.ACTION_REMOVE_FRIEND:
|
||||||
|
@ -13,18 +13,18 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program; if not, write to the Free Software
|
# along with this program; if not, write to the Free Software
|
||||||
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
import gobject
|
import gobject
|
||||||
import hippo
|
import hippo
|
||||||
|
|
||||||
from sugar.graphics.menu import Menu
|
from sugar.graphics.menu import Menu, MenuItem
|
||||||
from sugar.graphics.canvasicon import CanvasIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
|
from sugar.graphics import units
|
||||||
from sugar.presence import PresenceService
|
from sugar.presence import PresenceService
|
||||||
import _sugar
|
import _sugar
|
||||||
|
|
||||||
_ICON_SIZE = 75
|
|
||||||
|
|
||||||
class BuddyMenu(Menu):
|
class BuddyMenu(Menu):
|
||||||
ACTION_MAKE_FRIEND = 0
|
ACTION_MAKE_FRIEND = 0
|
||||||
ACTION_INVITE = 1
|
ACTION_INVITE = 1
|
||||||
@ -34,25 +34,26 @@ class BuddyMenu(Menu):
|
|||||||
self._buddy = buddy
|
self._buddy = buddy
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
|
|
||||||
|
Menu.__init__(self, buddy.get_name())
|
||||||
pixbuf = self._get_buddy_icon_pixbuf()
|
pixbuf = self._get_buddy_icon_pixbuf()
|
||||||
if pixbuf:
|
if pixbuf:
|
||||||
icon_item = hippo.CanvasImage()
|
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)
|
gtk.gdk.INTERP_BILINEAR)
|
||||||
del pixbuf
|
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
|
# FIXME: have to set the image _after_ adding the HippoCanvasImage
|
||||||
# to it's parent item, because that sets the HippoCanvasImage's context,
|
# to it's parent item, because that sets the HippoCanvasImage's context,
|
||||||
# which resets the object's 'image' property. Grr.
|
# which resets the object's 'image' property. Grr.
|
||||||
_sugar.hippo_canvas_image_set_image_from_gdk_pixbuf(icon_item, scaled_pixbuf)
|
_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)
|
self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb)
|
||||||
|
|
||||||
owner = shell.get_model().get_owner()
|
owner = shell.get_model().get_owner()
|
||||||
if buddy.get_name() != owner.get_name():
|
if buddy.get_name() != owner.get_name():
|
||||||
self._add_actions()
|
self._add_items()
|
||||||
|
|
||||||
def _get_buddy_icon_pixbuf(self):
|
def _get_buddy_icon_pixbuf(self):
|
||||||
buddy_object = self._buddy.get_buddy()
|
buddy_object = self._buddy.get_buddy()
|
||||||
@ -76,17 +77,19 @@ class BuddyMenu(Menu):
|
|||||||
del pbl
|
del pbl
|
||||||
return pixbuf
|
return pixbuf
|
||||||
|
|
||||||
def _add_actions(self):
|
def _add_items(self):
|
||||||
shell_model = self._shell.get_model()
|
shell_model = self._shell.get_model()
|
||||||
pservice = PresenceService.get_instance()
|
pservice = PresenceService.get_instance()
|
||||||
|
|
||||||
friends = shell_model.get_friends()
|
friends = shell_model.get_friends()
|
||||||
if friends.has_buddy(self._buddy):
|
if friends.has_buddy(self._buddy):
|
||||||
icon = CanvasIcon(icon_name='theme:stock-remove')
|
self.add_item(MenuItem(BuddyMenu.ACTION_REMOVE_FRIEND,
|
||||||
self.add_action(icon, BuddyMenu.ACTION_REMOVE_FRIEND)
|
_('Remove friend'),
|
||||||
|
'theme:stock-remove'))
|
||||||
else:
|
else:
|
||||||
icon = CanvasIcon(icon_name='theme:stock-add')
|
self.add_item(MenuItem(BuddyMenu.ACTION_MAKE_FRIEND,
|
||||||
self.add_action(icon, BuddyMenu.ACTION_MAKE_FRIEND)
|
_('Make friend'),
|
||||||
|
'theme:stock-add'))
|
||||||
|
|
||||||
activity = shell_model.get_home().get_current_activity()
|
activity = shell_model.get_home().get_current_activity()
|
||||||
if activity != None:
|
if activity != None:
|
||||||
@ -94,9 +97,9 @@ class BuddyMenu(Menu):
|
|||||||
|
|
||||||
# FIXME check that the buddy is not in the activity already
|
# FIXME check that the buddy is not in the activity already
|
||||||
|
|
||||||
icon = CanvasIcon(icon_name='theme:stock-invite')
|
self.add_item(MenuItem(BuddyMenu.ACTION_INVITE,
|
||||||
self.add_action(icon, BuddyMenu.ACTION_INVITE)
|
_('Invite'),
|
||||||
|
'theme:stock-invite'))
|
||||||
|
|
||||||
def __buddy_icon_changed_cb(self, buddy):
|
def __buddy_icon_changed_cb(self, buddy):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -20,10 +20,10 @@ import gobject
|
|||||||
import wnck
|
import wnck
|
||||||
|
|
||||||
from view.home.HomeWindow import HomeWindow
|
from view.home.HomeWindow import HomeWindow
|
||||||
from sugar.presence import PresenceService
|
from sugar.activity.activityhandle import ActivityHandle
|
||||||
from sugar.graphics.popupcontext import PopupContext
|
from sugar.graphics.popupcontext import PopupContext
|
||||||
from view.ActivityHost import ActivityHost
|
from view.ActivityHost import ActivityHost
|
||||||
from sugar.activity import ActivityFactory
|
from sugar.activity import activityfactory
|
||||||
from view.frame.frame import Frame
|
from view.frame.frame import Frame
|
||||||
from view.keyhandler import KeyHandler
|
from view.keyhandler import KeyHandler
|
||||||
from view.hardwaremanager import HardwareManager
|
from view.hardwaremanager import HardwareManager
|
||||||
@ -60,8 +60,6 @@ class Shell(gobject.GObject):
|
|||||||
self._frame = Frame(self)
|
self._frame = Frame(self)
|
||||||
self._frame.show_and_hide(3)
|
self._frame.show_and_hide(3)
|
||||||
|
|
||||||
self._pservice = PresenceService.get_instance()
|
|
||||||
|
|
||||||
self.start_activity('org.laptop.JournalActivity')
|
self.start_activity('org.laptop.JournalActivity')
|
||||||
|
|
||||||
def _activity_added_cb(self, home_model, home_activity):
|
def _activity_added_cb(self, home_model, home_activity):
|
||||||
@ -105,13 +103,8 @@ class Shell(gobject.GObject):
|
|||||||
def get_popup_context(self):
|
def get_popup_context(self):
|
||||||
return self._popup_context
|
return self._popup_context
|
||||||
|
|
||||||
def _join_success_cb(self, handler, activity, activity_ps, activity_id, activity_type):
|
def _join_error_cb(self, handler, err, home_model):
|
||||||
logging.debug("Joining activity %s (%s)" % (activity_id, activity_type))
|
home_mode.notify_activity_launch_failed(handler.get_activity_id())
|
||||||
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_activity(self, bundle_id, activity_id):
|
def join_activity(self, bundle_id, activity_id):
|
||||||
activity = self.get_activity(activity_id)
|
activity = self.get_activity(activity_id)
|
||||||
@ -119,11 +112,6 @@ class Shell(gobject.GObject):
|
|||||||
activity.present()
|
activity.present()
|
||||||
return
|
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
|
# Get the service name for this activity, if
|
||||||
# we have a bundle on the system capable of handling
|
# we have a bundle on the system capable of handling
|
||||||
# this activity type
|
# this activity type
|
||||||
@ -137,62 +125,25 @@ class Shell(gobject.GObject):
|
|||||||
home_model = self._model.get_home()
|
home_model = self._model.get_home()
|
||||||
home_model.notify_activity_launch(activity_id, act_type)
|
home_model.notify_activity_launch(activity_id, act_type)
|
||||||
|
|
||||||
handler = ActivityFactory.create(act_type)
|
handle = ActivityHandle(activity_id)
|
||||||
handler.connect('success', self._join_success_cb, activity_ps, activity_id, act_type)
|
handle.pservice_id = activity_id
|
||||||
handler.connect('error', self._join_error_cb, home_model, activity_id, act_type)
|
|
||||||
|
|
||||||
def _find_unique_activity_id(self):
|
handler = activityfactory.create(act_type, handle)
|
||||||
# create a new unique activity ID
|
handler.connect('error', self._join_error_cb, home_model)
|
||||||
i = 0
|
|
||||||
act_id = None
|
|
||||||
while i < 10:
|
|
||||||
act_id = sugar.util.unique_id()
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
# check through existing activities
|
def _start_error_cb(self, handler, err, home_model):
|
||||||
found = False
|
home_model.notify_activity_launch_failed(handler.get_activity_id())
|
||||||
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_activity(self, activity_type):
|
def start_activity(self, activity_type):
|
||||||
logging.debug('Shell.start_activity')
|
logging.debug('Shell.start_activity')
|
||||||
act_id = self._find_unique_activity_id()
|
|
||||||
if not act_id:
|
handler = activityfactory.create(activity_type)
|
||||||
logging.error("Couldn't find available activity ID.")
|
|
||||||
return None
|
|
||||||
|
|
||||||
home_model = self._model.get_home()
|
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.connect('error', self._start_error_cb, home_model)
|
||||||
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)
|
|
||||||
|
|
||||||
# Zoom to Home for launch feedback
|
# Zoom to Home for launch feedback
|
||||||
self.set_zoom_level(sugar.ZOOM_HOME)
|
self.set_zoom_level(sugar.ZOOM_HOME)
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from sugar.graphics.menuicon import MenuIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
from view.clipboardmenu import ClipboardMenu
|
from view.clipboardmenu import ClipboardMenu
|
||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
from sugar.activity import ActivityFactory
|
from sugar.activity import activityfactory
|
||||||
from sugar.clipboard import clipboardservice
|
from sugar.clipboard import clipboardservice
|
||||||
from sugar import util
|
from sugar import util
|
||||||
|
|
||||||
class ClipboardIcon(MenuIcon):
|
class ClipboardIcon(CanvasIcon):
|
||||||
|
|
||||||
def __init__(self, menu_shell, object_id, name):
|
def __init__(self, popup_context, object_id, name):
|
||||||
MenuIcon.__init__(self, menu_shell)
|
CanvasIcon.__init__(self)
|
||||||
|
self._popup_context = popup_context
|
||||||
self._object_id = object_id
|
self._object_id = object_id
|
||||||
self._name = name
|
self._name = name
|
||||||
self._percent = 0
|
self._percent = 0
|
||||||
@ -19,12 +20,15 @@ class ClipboardIcon(MenuIcon):
|
|||||||
self.connect('activated', self._icon_activated_cb)
|
self.connect('activated', self._icon_activated_cb)
|
||||||
self._menu = None
|
self._menu = None
|
||||||
|
|
||||||
def create_menu(self):
|
def get_popup(self):
|
||||||
self._menu = ClipboardMenu(self._name, self._percent, self._preview,
|
self._menu = ClipboardMenu(self._name, self._percent, self._preview,
|
||||||
self._activity)
|
self._activity)
|
||||||
self._menu.connect('action', self._popup_action_cb)
|
self._menu.connect('action', self._popup_action_cb)
|
||||||
return self._menu
|
return self._menu
|
||||||
|
|
||||||
|
def get_popup_context(self):
|
||||||
|
return self._popup_context
|
||||||
|
|
||||||
def set_state(self, name, percent, icon_name, preview, activity):
|
def set_state(self, name, percent, icon_name, preview, activity):
|
||||||
self._name = name
|
self._name = name
|
||||||
self._percent = percent
|
self._percent = percent
|
||||||
@ -53,15 +57,15 @@ class ClipboardIcon(MenuIcon):
|
|||||||
logging.debug("_icon_activated_cb: " + self._object_id)
|
logging.debug("_icon_activated_cb: " + self._object_id)
|
||||||
|
|
||||||
# Launch the activity to handle this item
|
# 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('success', self._activity_create_success_cb)
|
||||||
handler.connect('error', self._activity_create_error_cb)
|
handler.connect('error', self._activity_create_error_cb)
|
||||||
|
|
||||||
def _icon_activated_cb(self, icon):
|
def _icon_activated_cb(self, icon):
|
||||||
self._open_file()
|
self._open_file()
|
||||||
|
|
||||||
def _popup_action_cb(self, popup, action):
|
def _popup_action_cb(self, popup, menu_item):
|
||||||
self.popdown()
|
action = menu_item.props.action_id
|
||||||
|
|
||||||
if action == ClipboardMenu.ACTION_STOP_DOWNLOAD:
|
if action == ClipboardMenu.ACTION_STOP_DOWNLOAD:
|
||||||
raise "Stopping downloads still not implemented."
|
raise "Stopping downloads still not implemented."
|
||||||
|
@ -1,16 +1,23 @@
|
|||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
import hippo
|
import hippo
|
||||||
|
|
||||||
from sugar.graphics.menu import Menu
|
from sugar.graphics.menu import Menu, MenuItem
|
||||||
from sugar.graphics.canvasicon import CanvasIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
from sugar.graphics.ClipboardBubble import ClipboardBubble
|
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):
|
def __init__(self, percent = 0):
|
||||||
self._text_item = None
|
self._text_item = None
|
||||||
ClipboardBubble.__init__(self, percent=percent)
|
ClipboardBubble.__init__(self, percent=percent)
|
||||||
|
|
||||||
self._text_item = hippo.CanvasText(text=str(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)
|
self.append(self._text_item)
|
||||||
|
|
||||||
def do_set_property(self, pspec, value):
|
def do_set_property(self, pspec, value):
|
||||||
@ -30,57 +37,69 @@ class ClipboardMenu(Menu):
|
|||||||
Menu.__init__(self, name)
|
Menu.__init__(self, name)
|
||||||
|
|
||||||
if percent < 100:
|
if percent < 100:
|
||||||
self._progress_bar = ClipboardMenuItem(percent)
|
self._progress_bar = ClipboardProgressBar(percent)
|
||||||
self._root.append(self._progress_bar)
|
self.append(self._progress_bar)
|
||||||
else:
|
else:
|
||||||
self._progress_bar = None
|
self._progress_bar = None
|
||||||
|
|
||||||
self._remove_icon = None
|
self._remove_item = None
|
||||||
self._open_icon = None
|
self._open_item = None
|
||||||
self._stop_icon = None
|
self._stop_item = None
|
||||||
|
|
||||||
self.add_item(preview, wrap=True)
|
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)
|
self._update_icons(percent, activity)
|
||||||
|
|
||||||
def _update_icons(self, percent, activity):
|
def _update_icons(self, percent, activity):
|
||||||
|
|
||||||
if percent == 100 and activity:
|
if percent == 100 and activity:
|
||||||
if not self._remove_icon:
|
if not self._remove_item:
|
||||||
self._remove_icon = CanvasIcon(icon_name='theme:stock-remove')
|
self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE,
|
||||||
self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE)
|
_('Remove'),
|
||||||
|
'theme:stock-remove')
|
||||||
|
self.add_item(self._remove_item)
|
||||||
|
|
||||||
if not self._open_icon:
|
if not self._open_item:
|
||||||
self._open_icon = CanvasIcon(icon_name='theme:stock-keep')
|
self._open_item = MenuItem(ClipboardMenu.ACTION_OPEN,
|
||||||
self.add_action(self._open_icon, ClipboardMenu.ACTION_OPEN)
|
_('Open'),
|
||||||
|
'theme:stock-keep')
|
||||||
|
self.add_item(self._open_item)
|
||||||
|
|
||||||
if self._stop_icon:
|
if self._stop_item:
|
||||||
self.remove_action(self._stop_icon)
|
self.remove_item(self._stop_item)
|
||||||
self._stop_icon = None
|
self._stop_item = None
|
||||||
elif percent == 100 and not activity:
|
elif percent == 100 and not activity:
|
||||||
if not self._remove_icon:
|
if not self._remove_item:
|
||||||
self._remove_icon = CanvasIcon(icon_name='theme:stock-remove')
|
self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE,
|
||||||
self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE)
|
_('Remove'),
|
||||||
|
'theme:stock-remove')
|
||||||
|
self.add_item(self._remove_item)
|
||||||
|
|
||||||
if self._open_icon:
|
if self._open_item:
|
||||||
self.remove_action(self._open_icon)
|
self.remove_item(self._open_item)
|
||||||
self._open_icon = None
|
self._open_item = None
|
||||||
|
|
||||||
if self._stop_icon:
|
if self._stop_item:
|
||||||
self.remove_action(self._stop_icon)
|
self.remove_item(self._stop_item)
|
||||||
self._stop_icon = None
|
self._stop_item = None
|
||||||
else:
|
else:
|
||||||
if not self._stop_icon:
|
if not self._stop_item:
|
||||||
self._stop_icon = CanvasIcon(icon_name='theme:stock-close')
|
self._stop_item = MenuItem(ClipboardMenu.ACTION_STOP_DOWNLOAD,
|
||||||
self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD)
|
_('Stop download'),
|
||||||
|
'theme:stock-close')
|
||||||
|
self.add_item(self._stop_item)
|
||||||
|
|
||||||
if self._remove_icon:
|
if self._remove_item:
|
||||||
self.remove_action(self._remove_icon)
|
self.remove_item(self._remove_item)
|
||||||
self._remove_icon = None
|
self._remove_item = None
|
||||||
|
|
||||||
if self._open_icon:
|
if self._open_item:
|
||||||
self.remove_action(self._open_icon)
|
self.remove_item(self._open_item)
|
||||||
self._open_icon = None
|
self._open_item = None
|
||||||
|
|
||||||
def set_state(self, name, percent, preview, activity):
|
def set_state(self, name, percent, preview, activity):
|
||||||
self.set_title(name)
|
self.set_title(name)
|
||||||
|
@ -21,6 +21,7 @@ from sugar.graphics import units
|
|||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
from sugar.graphics.iconbutton import IconButton
|
from sugar.graphics.iconbutton import IconButton
|
||||||
from sugar.presence import PresenceService
|
from sugar.presence import PresenceService
|
||||||
|
from sugar.activity import bundleregistry
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
|
|
||||||
class ActivityButton(IconButton):
|
class ActivityButton(IconButton):
|
||||||
@ -63,7 +64,7 @@ class ActivitiesBox(hippo.CanvasBox):
|
|||||||
self._invite_to_item = {}
|
self._invite_to_item = {}
|
||||||
self._invites = self._shell_model.get_invites()
|
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:
|
for bundle in bundle_registry:
|
||||||
if bundle.get_show_launcher():
|
if bundle.get_show_launcher():
|
||||||
self.add_activity(bundle)
|
self.add_activity(bundle)
|
||||||
|
@ -10,4 +10,5 @@ sugar_PYTHON = \
|
|||||||
ZoomBox.py \
|
ZoomBox.py \
|
||||||
notificationtray.py \
|
notificationtray.py \
|
||||||
overlaybox.py \
|
overlaybox.py \
|
||||||
PanelWindow.py
|
PanelWindow.py \
|
||||||
|
framepopupcontext.py
|
||||||
|
@ -17,7 +17,6 @@
|
|||||||
import gtk
|
import gtk
|
||||||
import hippo
|
import hippo
|
||||||
|
|
||||||
from sugar.graphics.menushell import MenuShell
|
|
||||||
from sugar.graphics import units
|
from sugar.graphics import units
|
||||||
|
|
||||||
class PanelWindow(gtk.Window):
|
class PanelWindow(gtk.Window):
|
||||||
@ -51,13 +50,8 @@ class PanelWindow(gtk.Window):
|
|||||||
self.add(self._canvas)
|
self.add(self._canvas)
|
||||||
self._canvas.show()
|
self._canvas.show()
|
||||||
|
|
||||||
self._menu_shell = MenuShell(self._canvas)
|
|
||||||
|
|
||||||
self.resize(width, height)
|
self.resize(width, height)
|
||||||
|
|
||||||
def get_menu_shell(self):
|
|
||||||
return self._menu_shell
|
|
||||||
|
|
||||||
def get_root(self):
|
def get_root(self):
|
||||||
return self._bg
|
return self._bg
|
||||||
|
|
||||||
|
@ -19,31 +19,32 @@ from gettext import gettext as _
|
|||||||
|
|
||||||
import hippo
|
import hippo
|
||||||
|
|
||||||
from sugar.graphics.popup import Popup
|
from sugar.graphics.menu import Menu, MenuItem
|
||||||
from sugar.graphics.menuicon import MenuIcon
|
|
||||||
from sugar.graphics.menu import Menu
|
|
||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
from sugar.graphics.iconbutton import IconButton
|
from sugar.graphics.iconbutton import IconButton
|
||||||
import sugar
|
import sugar
|
||||||
|
|
||||||
class ActivityPopup(Popup):
|
class ActivityMenu(Menu):
|
||||||
ACTION_SHARE = 1
|
ACTION_SHARE = 1
|
||||||
ACTION_CLOSE = 2
|
ACTION_CLOSE = 2
|
||||||
|
|
||||||
def __init__(self, activity_model):
|
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():
|
if not activity_model.get_shared():
|
||||||
self.add_item(ActivityPopup.ACTION_SHARE, _('Share'),
|
self.add_item(MenuItem(ActivityMenu.ACTION_SHARE,
|
||||||
'theme:stock-share-mesh')
|
_('Share'),
|
||||||
|
'theme:stock-share-mesh'))
|
||||||
|
|
||||||
self.add_item(ActivityPopup.ACTION_CLOSE, _('Close'),
|
self.add_item(MenuItem(ActivityMenu.ACTION_CLOSE,
|
||||||
'theme:stock-close')
|
_('Close'),
|
||||||
|
'theme:stock-close'))
|
||||||
|
|
||||||
class ActivityButton(IconButton):
|
class ActivityButton(IconButton):
|
||||||
def __init__(self, shell, activity_model):
|
def __init__(self, shell, activity_model, popup_context):
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
self._activity_model = activity_model
|
self._activity_model = activity_model
|
||||||
|
self._popup_context = popup_context
|
||||||
|
|
||||||
icon_name = self._activity_model.get_icon_name()
|
icon_name = self._activity_model.get_icon_name()
|
||||||
icon_color = self._activity_model.get_icon_color()
|
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)
|
IconButton.__init__(self, icon_name=icon_name, color=icon_color)
|
||||||
|
|
||||||
def get_popup(self):
|
def get_popup(self):
|
||||||
popup = ActivityPopup(self._activity_model)
|
menu = ActivityMenu(self._activity_model)
|
||||||
#popup.connect('action', self._action_cb)
|
menu.connect('action', self._action_cb)
|
||||||
return popup
|
return menu
|
||||||
|
|
||||||
def get_popup_context(self):
|
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
|
# TODO: Wouldn't be better to share/close the activity associated with
|
||||||
# this button instead of asking for the current activity?
|
# this button instead of asking for the current activity?
|
||||||
activity = self._shell.get_current_activity()
|
activity = self._shell.get_current_activity()
|
||||||
@ -68,17 +67,17 @@ class ActivityButton(IconButton):
|
|||||||
logging.error('No active activity.')
|
logging.error('No active activity.')
|
||||||
return
|
return
|
||||||
|
|
||||||
if action_id == ActivityPopup.ACTION_SHARE:
|
if menu_item.props.action_id == ActivityMenu.ACTION_SHARE:
|
||||||
activity.share()
|
activity.share()
|
||||||
elif action_id == ActivityPopup.ACTION_CLOSE:
|
elif menu_item.props.action_id == ActivityMenu.ACTION_CLOSE:
|
||||||
activity.close()
|
activity.close()
|
||||||
|
|
||||||
class ZoomBox(hippo.CanvasBox):
|
class ZoomBox(hippo.CanvasBox):
|
||||||
def __init__(self, shell, menu_shell):
|
def __init__(self, shell, popup_context):
|
||||||
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
|
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
|
||||||
|
|
||||||
self._shell = shell
|
self._shell = shell
|
||||||
self._menu_shell = menu_shell
|
self._popup_context = popup_context
|
||||||
self._activity_icon = None
|
self._activity_icon = None
|
||||||
|
|
||||||
icon = IconButton(icon_name='theme:stock-zoom-mesh')
|
icon = IconButton(icon_name='theme:stock-zoom-mesh')
|
||||||
@ -107,7 +106,7 @@ class ZoomBox(hippo.CanvasBox):
|
|||||||
self.remove(self._activity_icon)
|
self.remove(self._activity_icon)
|
||||||
|
|
||||||
if home_activity:
|
if home_activity:
|
||||||
icon = ActivityButton(self._shell, home_activity)
|
icon = ActivityButton(self._shell, home_activity, self._popup_context)
|
||||||
self.append(icon)
|
self.append(icon)
|
||||||
self._activity_icon = icon
|
self._activity_icon = icon
|
||||||
else:
|
else:
|
||||||
|
@ -37,9 +37,9 @@ class _ContextMap:
|
|||||||
|
|
||||||
class ClipboardBox(hippo.CanvasBox):
|
class ClipboardBox(hippo.CanvasBox):
|
||||||
|
|
||||||
def __init__(self, menu_shell):
|
def __init__(self, popup_context):
|
||||||
hippo.CanvasBox.__init__(self)
|
hippo.CanvasBox.__init__(self)
|
||||||
self._menu_shell = menu_shell
|
self._popup_context = popup_context
|
||||||
self._icons = {}
|
self._icons = {}
|
||||||
self._context_map = _ContextMap()
|
self._context_map = _ContextMap()
|
||||||
|
|
||||||
@ -74,7 +74,7 @@ class ClipboardBox(hippo.CanvasBox):
|
|||||||
on_disk = False)
|
on_disk = False)
|
||||||
|
|
||||||
def _object_added_cb(self, cb_service, object_id, name):
|
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.append(icon)
|
||||||
self._icons[object_id] = icon
|
self._icons[object_id] = icon
|
||||||
|
|
||||||
|
@ -17,10 +17,9 @@ class ClipboardPanelWindow(PanelWindow):
|
|||||||
clipboard = gtk.Clipboard()
|
clipboard = gtk.Clipboard()
|
||||||
clipboard.connect("owner-change", self._owner_change_cb)
|
clipboard.connect("owner-change", self._owner_change_cb)
|
||||||
|
|
||||||
menu_shell = self.get_menu_shell()
|
|
||||||
root = self.get_root()
|
root = self.get_root()
|
||||||
|
|
||||||
box = ClipboardBox(menu_shell)
|
box = ClipboardBox(frame.get_popup_context())
|
||||||
root.append(box)
|
root.append(box)
|
||||||
|
|
||||||
# Receiving dnd drops
|
# Receiving dnd drops
|
||||||
|
@ -27,9 +27,9 @@ from view.frame.FriendsBox import FriendsBox
|
|||||||
from view.frame.PanelWindow import PanelWindow
|
from view.frame.PanelWindow import PanelWindow
|
||||||
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
from view.frame.clipboardpanelwindow import ClipboardPanelWindow
|
||||||
from view.frame.notificationtray import NotificationTray
|
from view.frame.notificationtray import NotificationTray
|
||||||
|
from view.frame.framepopupcontext import FramePopupContext
|
||||||
from model.ShellModel import ShellModel
|
from model.ShellModel import ShellModel
|
||||||
from sugar.graphics.timeline import Timeline
|
from sugar.graphics.timeline import Timeline
|
||||||
from sugar.graphics.menushell import MenuShell
|
|
||||||
from sugar.graphics import units
|
from sugar.graphics import units
|
||||||
|
|
||||||
_ANIMATION = False
|
_ANIMATION = False
|
||||||
@ -62,6 +62,12 @@ class Frame:
|
|||||||
self._event_frame.connect('leave', self._event_frame_leave_cb)
|
self._event_frame.connect('leave', self._event_frame_leave_cb)
|
||||||
self._event_frame.show()
|
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._top_panel = self._create_top_panel()
|
||||||
self._bottom_panel = self._create_bottom_panel()
|
self._bottom_panel = self._create_bottom_panel()
|
||||||
self._left_panel = self._create_left_panel()
|
self._left_panel = self._create_left_panel()
|
||||||
@ -70,20 +76,11 @@ class Frame:
|
|||||||
shell.get_model().connect('notify::state',
|
shell.get_model().connect('notify::state',
|
||||||
self._shell_state_changed_cb)
|
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):
|
def _create_top_panel(self):
|
||||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||||
menu_shell = panel.get_menu_shell()
|
|
||||||
root = panel.get_root()
|
root = panel.get_root()
|
||||||
|
|
||||||
menu_shell.set_position(MenuShell.BOTTOM)
|
box = ZoomBox(self._shell, self._popup_context)
|
||||||
|
|
||||||
box = ZoomBox(self._shell, menu_shell)
|
|
||||||
root.append(box)
|
root.append(box)
|
||||||
|
|
||||||
tray = NotificationTray()
|
tray = NotificationTray()
|
||||||
@ -103,11 +100,8 @@ class Frame:
|
|||||||
|
|
||||||
def _create_bottom_panel(self):
|
def _create_bottom_panel(self):
|
||||||
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL)
|
||||||
menu_shell = panel.get_menu_shell()
|
|
||||||
root = panel.get_root()
|
root = panel.get_root()
|
||||||
|
|
||||||
menu_shell.set_position(MenuShell.TOP)
|
|
||||||
|
|
||||||
box = ActivitiesBox(self._shell)
|
box = ActivitiesBox(self._shell)
|
||||||
root.append(box)
|
root.append(box)
|
||||||
|
|
||||||
@ -115,12 +109,9 @@ class Frame:
|
|||||||
|
|
||||||
def _create_right_panel(self):
|
def _create_right_panel(self):
|
||||||
panel = self._create_panel(hippo.ORIENTATION_VERTICAL)
|
panel = self._create_panel(hippo.ORIENTATION_VERTICAL)
|
||||||
menu_shell = panel.get_menu_shell()
|
|
||||||
root = panel.get_root()
|
root = panel.get_root()
|
||||||
|
|
||||||
menu_shell.set_position(MenuShell.LEFT)
|
box = FriendsBox(self._shell, self._popup_context)
|
||||||
|
|
||||||
box = FriendsBox(self._shell, menu_shell)
|
|
||||||
root.append(box)
|
root.append(box)
|
||||||
|
|
||||||
return panel
|
return panel
|
||||||
@ -158,19 +149,6 @@ class Frame:
|
|||||||
panel.connect('enter-notify-event', self._enter_notify_cb)
|
panel.connect('enter-notify-event', self._enter_notify_cb)
|
||||||
panel.connect('leave-notify-event', self._leave_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):
|
def _popup_context_activated_cb(self, popup_context):
|
||||||
self._timeline.goto('slide_in', True)
|
self._timeline.goto('slide_in', True)
|
||||||
|
|
||||||
@ -205,8 +183,7 @@ class Frame:
|
|||||||
|
|
||||||
def _leave_notify(self, panel):
|
def _leave_notify(self, panel):
|
||||||
self._hover_frame = False
|
self._hover_frame = False
|
||||||
if not panel.get_menu_shell().is_active() and \
|
if not self._popup_context.is_active() and \
|
||||||
not self._shell.get_popup_context().is_active() and \
|
|
||||||
(self._mode == Frame.HIDE_ON_LEAVE or \
|
(self._mode == Frame.HIDE_ON_LEAVE or \
|
||||||
self._mode == Frame.AUTOMATIC):
|
self._mode == Frame.AUTOMATIC):
|
||||||
self._timeline.play('before_slide_out', 'slide_out')
|
self._timeline.play('before_slide_out', 'slide_out')
|
||||||
@ -281,3 +258,6 @@ class Frame:
|
|||||||
|
|
||||||
def is_visible(self):
|
def is_visible(self):
|
||||||
return self._top_panel.props.visible
|
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
|
sugardir = $(pythondir)/sugar/activity
|
||||||
sugar_PYTHON = \
|
sugar_PYTHON = \
|
||||||
__init__.py \
|
__init__.py \
|
||||||
Activity.py \
|
activity.py \
|
||||||
ActivityFactory.py \
|
activityfactory.py \
|
||||||
|
activityfactoryservice.py \
|
||||||
|
activityhandle.py \
|
||||||
|
activityservice.py \
|
||||||
bundle.py \
|
bundle.py \
|
||||||
bundlebuilder.py \
|
bundlebuilder.py \
|
||||||
bundleregistry.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 logging
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from ConfigParser import ConfigParser
|
from ConfigParser import ConfigParser
|
||||||
|
|
||||||
|
from sugar import env
|
||||||
|
|
||||||
|
_PYTHON_FACTORY='sugar-activity-factory'
|
||||||
|
|
||||||
class Bundle:
|
class Bundle:
|
||||||
"""Info about an activity bundle. Wraps the activity.info file."""
|
"""Info about an activity bundle. Wraps the activity.info file."""
|
||||||
def __init__(self, path):
|
def __init__(self, path):
|
||||||
@ -38,11 +41,18 @@ class Bundle:
|
|||||||
self._valid = False
|
self._valid = False
|
||||||
logging.error('%s must specify a name' % self._path)
|
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')
|
self._exec = cp.get(section, 'exec')
|
||||||
else:
|
else:
|
||||||
|
self._exec = None
|
||||||
self._valid = False
|
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.has_option(section, 'show_launcher'):
|
||||||
if cp.get(section, 'show_launcher') == 'no':
|
if cp.get(section, 'show_launcher') == 'no':
|
||||||
@ -71,6 +81,10 @@ class Bundle:
|
|||||||
"""Get the activity service name"""
|
"""Get the activity service name"""
|
||||||
return self._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):
|
def get_default_type(self):
|
||||||
"""Get the type of the main network service which tracks presence
|
"""Get the type of the main network service which tracks presence
|
||||||
and provides info about the activity, for example the title."""
|
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"""
|
"""Get the command to execute to launch the activity factory"""
|
||||||
return self._exec
|
return self._exec
|
||||||
|
|
||||||
|
def get_class(self):
|
||||||
|
"""Get the main Activity class"""
|
||||||
|
return self._exec
|
||||||
|
|
||||||
def get_show_launcher(self):
|
def get_show_launcher(self):
|
||||||
"""Get whether there should be a visible launcher for the activity"""
|
"""Get whether there should be a visible launcher for the activity"""
|
||||||
return self._show_launcher
|
return self._show_launcher
|
||||||
|
@ -11,13 +11,8 @@ class _ServiceManager(object):
|
|||||||
self._path = env.get_user_service_dir()
|
self._path = env.get_user_service_dir()
|
||||||
|
|
||||||
def add(self, bundle):
|
def add(self, bundle):
|
||||||
name = bundle.get_service_name()
|
util.write_service(bundle.get_service_name(),
|
||||||
|
bundle.get_exec(), self._path)
|
||||||
# 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)
|
|
||||||
|
|
||||||
class BundleRegistry(gobject.GObject):
|
class BundleRegistry(gobject.GObject):
|
||||||
"""Service that tracks the available activity bundles"""
|
"""Service that tracks the available activity bundles"""
|
||||||
@ -73,3 +68,14 @@ class BundleRegistry(gobject.GObject):
|
|||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
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:
|
except ImportError:
|
||||||
from sugar.__installed__ import *
|
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():
|
def is_emulator():
|
||||||
if os.environ.has_key('SUGAR_EMULATOR'):
|
if os.environ.has_key('SUGAR_EMULATOR'):
|
||||||
if os.environ['SUGAR_EMULATOR'] == 'yes':
|
if os.environ['SUGAR_EMULATOR'] == 'yes':
|
||||||
return True
|
return True
|
||||||
return False
|
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():
|
def get_profile_path():
|
||||||
if os.environ.has_key('SUGAR_PROFILE'):
|
if os.environ.has_key('SUGAR_PROFILE'):
|
||||||
profile_id = os.environ['SUGAR_PROFILE']
|
profile_id = os.environ['SUGAR_PROFILE']
|
||||||
|
@ -12,7 +12,6 @@ sugar_PYTHON = \
|
|||||||
iconcolor.py \
|
iconcolor.py \
|
||||||
label.py \
|
label.py \
|
||||||
menu.py \
|
menu.py \
|
||||||
menuicon.py \
|
|
||||||
menushell.py \
|
menushell.py \
|
||||||
optionmenu.py \
|
optionmenu.py \
|
||||||
roundbox.py \
|
roundbox.py \
|
||||||
|
@ -25,6 +25,7 @@ import cairo
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
from sugar.graphics.iconcolor import IconColor
|
from sugar.graphics.iconcolor import IconColor
|
||||||
|
from sugar.graphics.timeline import Timeline
|
||||||
|
|
||||||
class _IconCacheIcon:
|
class _IconCacheIcon:
|
||||||
def __init__(self, name, color, now):
|
def __init__(self, name, color, now):
|
||||||
@ -152,9 +153,17 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
self._icon_name = None
|
self._icon_name = None
|
||||||
self._cache = False
|
self._cache = False
|
||||||
self._handle = None
|
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)
|
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)
|
self.connect('button-press-event', self._button_press_event_cb)
|
||||||
|
|
||||||
def _clear_buffers(self):
|
def _clear_buffers(self):
|
||||||
@ -261,3 +270,80 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
|
|
||||||
def _button_press_event_cb(self, item, event):
|
def _button_press_event_cb(self, item, event):
|
||||||
item.emit_activated()
|
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 canvasicon import CanvasIcon
|
||||||
from iconcolor import IconColor
|
from iconcolor import IconColor
|
||||||
from sugar.graphics import units
|
from sugar.graphics import units
|
||||||
from sugar.graphics.timeline import Timeline
|
|
||||||
from sugar import profile
|
from sugar import profile
|
||||||
|
|
||||||
STANDARD_SIZE = 0
|
STANDARD_SIZE = 0
|
||||||
@ -49,100 +48,14 @@ class IconButton(CanvasIcon):
|
|||||||
self._prelight_color = profile.get_color()
|
self._prelight_color = profile.get_color()
|
||||||
self._inactive_color = IconColor('#808080,#424242')
|
self._inactive_color = IconColor('#808080,#424242')
|
||||||
self._active = True
|
self._active = True
|
||||||
self._popup = None
|
|
||||||
self._hover_popup = False
|
|
||||||
|
|
||||||
CanvasIcon.__init__(self, icon_name=icon_name, cache=True,
|
CanvasIcon.__init__(self, icon_name=icon_name, cache=True,
|
||||||
color=self._normal_color)
|
color=self._normal_color)
|
||||||
|
|
||||||
self._set_size(STANDARD_SIZE)
|
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)
|
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):
|
def _set_size(self, size):
|
||||||
if size == SMALL_SIZE:
|
if size == SMALL_SIZE:
|
||||||
self.props.box_width = -1
|
self.props.box_width = -1
|
||||||
@ -173,8 +86,9 @@ class IconButton(CanvasIcon):
|
|||||||
elif pspec.name == 'active':
|
elif pspec.name == 'active':
|
||||||
return self._active
|
return self._active
|
||||||
else:
|
else:
|
||||||
return CanvasIcon.get_property(self, pspec)
|
return CanvasIcon.do_get_property(self, pspec)
|
||||||
|
|
||||||
def _button_press_event_cb(self, widget, event):
|
def _button_press_event_cb(self, widget, event):
|
||||||
if self._active:
|
if self._active:
|
||||||
self.emit_activated()
|
self.emit_activated()
|
||||||
|
return True
|
||||||
|
@ -14,94 +14,98 @@
|
|||||||
# License along with this library; if not, write to the
|
# License along with this library; if not, write to the
|
||||||
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
# Boston, MA 02111-1307, USA.
|
# Boston, MA 02111-1307, USA.
|
||||||
|
import sys
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
import hippo
|
import hippo
|
||||||
import gobject
|
import gobject
|
||||||
|
|
||||||
from sugar.graphics.canvasicon import CanvasIcon
|
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):
|
class MenuItem(hippo.CanvasBox):
|
||||||
__gsignals__ = {
|
__gtype_name__ = 'SugarMenuItem'
|
||||||
'action': (gobject.SIGNAL_RUN_FIRST,
|
|
||||||
gobject.TYPE_NONE, ([int])),
|
__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):
|
def __init__(self, action_id, label, icon_name=None, icon_color=None):
|
||||||
gtk.Window.__init__(self, gtk.WINDOW_POPUP)
|
hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL)
|
||||||
|
|
||||||
canvas = hippo.Canvas()
|
self._action_id = action_id
|
||||||
self.add(canvas)
|
self.props.padding = 5
|
||||||
canvas.show()
|
self.props.spacing = 5
|
||||||
|
|
||||||
self._root = hippo.CanvasBox()
|
if icon_name:
|
||||||
canvas.set_root(self._root)
|
icon = CanvasIcon(icon_name=icon_name,
|
||||||
|
scale=units.SMALL_ICON_SCALE)
|
||||||
|
if icon_color:
|
||||||
|
icon.props.color = icon_color
|
||||||
|
self.append(icon)
|
||||||
|
|
||||||
|
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:
|
if title:
|
||||||
self._title_item = hippo.CanvasText(text=title)
|
title_item = hippo.CanvasText(text=title)
|
||||||
self._root.append(self._title_item)
|
title_item.props.color = color.LABEL_TEXT.get_int()
|
||||||
else:
|
title_item.props.font_desc = font.DEFAULT.get_pango_desc()
|
||||||
self._title_item = None
|
self.append(title_item)
|
||||||
|
self.add_separator()
|
||||||
|
|
||||||
if content_box:
|
def add_item(self, item):
|
||||||
separator = self._create_separator()
|
item.connect('button-press-event', self._item_button_press_event_cb)
|
||||||
self._root.append(separator)
|
self.append(item)
|
||||||
self._root.append(content_box)
|
|
||||||
|
|
||||||
self._action_box = None
|
def remove_item(self, item):
|
||||||
self._item_box = None
|
self.remove(item)
|
||||||
|
|
||||||
def _create_separator(self):
|
def add_separator(self):
|
||||||
separator = hippo.CanvasBox()
|
box = hippo.CanvasBox()
|
||||||
return separator
|
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):
|
def _item_button_press_event_cb(self, menu_item, event):
|
||||||
if self._title_item:
|
self.emit('action', menu_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)
|
|
||||||
|
@ -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 import units
|
||||||
from sugar.graphics.roundbox import RoundBox
|
from sugar.graphics.roundbox import RoundBox
|
||||||
|
from sugar.graphics.menu import Menu, MenuItem
|
||||||
from sugar.graphics import iconbutton
|
from sugar.graphics import iconbutton
|
||||||
from sugar.graphics import color
|
from sugar.graphics import color
|
||||||
from sugar.graphics import font
|
from sugar.graphics import font
|
||||||
from sugar.graphics.canvasicon import CanvasIcon
|
from sugar.graphics.canvasicon import CanvasIcon
|
||||||
|
|
||||||
class Menu(hippo.CanvasBox, hippo.CanvasItem):
|
class _Menu(Menu):
|
||||||
__gtype_name__ = 'SugarMenu'
|
|
||||||
|
|
||||||
__gsignals__ = {
|
|
||||||
'action': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object]))
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
hippo.CanvasBox.__init__(self)
|
Menu.__init__(self)
|
||||||
self.props.background_color = color.MENU_BACKGROUND.get_int()
|
self._is_visible = False
|
||||||
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()
|
|
||||||
|
|
||||||
def is_visible(self):
|
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):
|
class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
|
||||||
__gtype_name__ = 'SugarOptionMenu'
|
__gtype_name__ = 'SugarOptionMenu'
|
||||||
@ -122,8 +79,9 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
arrow.props.xalign = hippo.ALIGNMENT_START
|
arrow.props.xalign = hippo.ALIGNMENT_START
|
||||||
self._round_box.append(arrow)
|
self._round_box.append(arrow)
|
||||||
|
|
||||||
self._menu = Menu()
|
self._menu = _Menu()
|
||||||
self._menu.connect('action', self._menu_action_cb)
|
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)
|
self.connect('button-press-event', self._button_press_event_cb)
|
||||||
|
|
||||||
@ -135,33 +93,37 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
if pspec.name == 'value':
|
if pspec.name == 'value':
|
||||||
return self._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:
|
if not self._value:
|
||||||
self._value = action_id
|
self._value = menu_item.props.action_id
|
||||||
self._canvas_text.props.text = label
|
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):
|
def add_separator(self):
|
||||||
self._menu.add_separator()
|
self._menu.add_separator()
|
||||||
|
|
||||||
def _button_press_event_cb(self, box, event):
|
def _button_press_event_cb(self, box, event):
|
||||||
if self._menu.is_visible():
|
if self._menu.is_visible():
|
||||||
self._menu.hide()
|
self._menu.popdown()
|
||||||
else:
|
else:
|
||||||
context = self._round_box.get_context()
|
context = self._round_box.get_context()
|
||||||
#[x, y] = context.translate_to_screen(self._round_box)
|
[x, y] = context.translate_to_screen(self._round_box)
|
||||||
[x, y] = context.translate_to_widget(self._round_box)
|
|
||||||
|
|
||||||
# TODO: Any better place to do this?
|
# TODO: Any better place to do this?
|
||||||
self._menu.props.box_width = self.get_width_request()
|
self._menu.props.box_width = self.get_width_request()
|
||||||
|
|
||||||
[width, height] = self._round_box.get_allocation()
|
[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, menu_item):
|
||||||
|
action_id = menu_item.props.action_id
|
||||||
|
label = menu_item.props.label
|
||||||
|
|
||||||
def _menu_action_cb(self, menu, data):
|
|
||||||
[action_id, label] = data
|
|
||||||
if action_id != self._value:
|
if action_id != self._value:
|
||||||
self._value = action_id
|
self._value = action_id
|
||||||
self._canvas_text.props.text = label
|
self._canvas_text.props.text = label
|
||||||
self.emit('changed')
|
self.emit('changed')
|
||||||
|
|
||||||
|
def _menu_action_completed_cb(self, menu):
|
||||||
|
self._menu.popdown()
|
||||||
|
@ -21,13 +21,6 @@ import gobject
|
|||||||
import gtk
|
import gtk
|
||||||
import hippo
|
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):
|
class Popup(hippo.CanvasBox, hippo.CanvasItem):
|
||||||
__gtype_name__ = 'SugarPopup'
|
__gtype_name__ = 'SugarPopup'
|
||||||
|
|
||||||
@ -35,38 +28,10 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
'action-completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
|
'action-completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, title):
|
def __init__(self):
|
||||||
hippo.CanvasBox.__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
|
self._window = None
|
||||||
|
self.connect('button-press-event', self._button_press_event_cb)
|
||||||
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)
|
|
||||||
|
|
||||||
def popup(self, x, y):
|
def popup(self, x, y):
|
||||||
if not self._window:
|
if not self._window:
|
||||||
@ -80,5 +45,5 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem):
|
|||||||
self._window.destroy()
|
self._window.destroy()
|
||||||
self._window = None
|
self._window = None
|
||||||
|
|
||||||
def _item_button_press_event_cb(self, item, event):
|
def _button_press_event_cb(self, menu, event):
|
||||||
self.emit('action-completed')
|
self.emit('action-completed')
|
||||||
|
@ -24,6 +24,7 @@ import hippo
|
|||||||
|
|
||||||
from sugar.graphics.toolbar import Toolbar
|
from sugar.graphics.toolbar import Toolbar
|
||||||
from sugar.graphics.optionmenu import OptionMenu
|
from sugar.graphics.optionmenu import OptionMenu
|
||||||
|
from sugar.graphics.menu import MenuItem
|
||||||
from sugar.graphics.iconbutton import IconButton
|
from sugar.graphics.iconbutton import IconButton
|
||||||
|
|
||||||
def _option_menu_changed_cb(option_menu):
|
def _option_menu_changed_cb(option_menu):
|
||||||
@ -56,11 +57,11 @@ OPTION_WRITE = 3
|
|||||||
OPTION_CHAT = 4
|
OPTION_CHAT = 4
|
||||||
|
|
||||||
option_menu = OptionMenu()
|
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_separator()
|
||||||
option_menu.add_option(OPTION_DRAW, _('Draw'), 'theme:stock-close')
|
option_menu.add_item(MenuItem(OPTION_DRAW, _('Draw'), 'theme:stock-close'))
|
||||||
option_menu.add_option(OPTION_WRITE, _('Write'))
|
option_menu.add_item(MenuItem(OPTION_WRITE, _('Write')))
|
||||||
option_menu.add_option(OPTION_CHAT, _('Chat'))
|
option_menu.add_item(MenuItem(OPTION_CHAT, _('Chat')))
|
||||||
option_menu.connect('changed', _option_menu_changed_cb)
|
option_menu.connect('changed', _option_menu_changed_cb)
|
||||||
toolbar.append(option_menu)
|
toolbar.append(option_menu)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user