From 00db5e15a50e3bf881dae3aed88810a274d005a4 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 14:13:52 +0100 Subject: [PATCH 01/29] Move popup activation logic from IconButton to CanvasIcon. --- sugar/graphics/canvasicon.py | 87 ++++++++++++++++++++++++++++++++++++ sugar/graphics/iconbutton.py | 87 +----------------------------------- 2 files changed, 88 insertions(+), 86 deletions(-) diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py index 66ff2350..020e966d 100644 --- a/sugar/graphics/canvasicon.py +++ b/sugar/graphics/canvasicon.py @@ -25,6 +25,7 @@ import cairo import time from sugar.graphics.iconcolor import IconColor +from sugar.graphics.timeline import Timeline class _IconCacheIcon: def __init__(self, name, color, now): @@ -152,9 +153,17 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): self._icon_name = None self._cache = False self._handle = None + self._popup = None + self._hover_popup = False + + self._timeline = Timeline(self) + self._timeline.add_tag('popup', 6, 6) + self._timeline.add_tag('before_popdown', 7, 7) + self._timeline.add_tag('popdown', 8, 8) hippo.CanvasBox.__init__(self, **kwargs) + self.connect('motion-notify-event', self._motion_notify_event_cb) self.connect('button-press-event', self._button_press_event_cb) def _clear_buffers(self): @@ -261,3 +270,81 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): def _button_press_event_cb(self, item, event): item.emit_activated() + + def get_popup(self): + return self._popup + + def get_popup_context(self): + return None + + def do_popup(self, current, n_frames): + if self._popup: + return + + popup = self.get_popup() + if not popup: + return + + popup_context = self.get_popup_context() + + [x, y] = [None, None] + if popup_context: + try: + [x, y] = popup_context.get_position(self, popup) + except NotImplementedError: + pass + + if [x, y] == [None, None]: + context = self.get_context() + #[x, y] = context.translate_to_screen(self) + [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() diff --git a/sugar/graphics/iconbutton.py b/sugar/graphics/iconbutton.py index ffed0adf..57c0712e 100644 --- a/sugar/graphics/iconbutton.py +++ b/sugar/graphics/iconbutton.py @@ -49,100 +49,14 @@ class IconButton(CanvasIcon): self._prelight_color = profile.get_color() self._inactive_color = IconColor('#808080,#424242') self._active = True - self._popup = None - self._hover_popup = False CanvasIcon.__init__(self, icon_name=icon_name, cache=True, color=self._normal_color) self._set_size(STANDARD_SIZE) - self._timeline = Timeline(self) - self._timeline.add_tag('popup', 6, 6) - self._timeline.add_tag('before_popdown', 7, 7) - self._timeline.add_tag('popdown', 8, 8) - - self.connect('motion-notify-event', self._motion_notify_event_cb) self.connect('button-press-event', self._button_press_event_cb) - def get_popup(self): - return self._popup - - def get_popup_context(self): - return None - - def do_popup(self, current, n_frames): - if self._popup: - return - - popup = self.get_popup() - if not popup: - return - - popup_context = self.get_popup_context() - - [x, y] = [None, None] - if popup_context: - try: - [x, y] = popup_context.get_position(self, popup) - except NotImplementedError: - pass - - if [x, y] == [None, None]: - context = self.get_context() - #[x, y] = context.translate_to_screen(self) - [x, y] = context.translate_to_widget(self) - - # TODO: Any better place to do this? - popup.props.box_width = max(popup.props.box_width, - self.get_width_request()) - - [width, height] = self.get_allocation() - y += height - position = [x, y] - - popup.popup(x, y) - popup.connect('motion-notify-event', - self._popup_motion_notify_event_cb) - popup.connect('action-completed', - self._popup_action_completed_cb) - - if popup_context: - popup_context.popped_up(popup) - - self._popup = popup - - def do_popdown(self, current, frame): - if self._popup: - self._popup.popdown() - - popup_context = self.get_popup_context() - if popup_context: - popup_context.popped_down(self._popup) - - self._popup = None - - def popdown(self): - self._timeline.play('popdown', 'popdown') - - def _motion_notify_event_cb(self, button, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - self._timeline.play(None, 'popup') - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - if not self._hover_popup: - self._timeline.play('before_popdown', 'popdown') - - def _popup_motion_notify_event_cb(self, popup, event): - if event.detail == hippo.MOTION_DETAIL_ENTER: - self._hover_popup = True - self._timeline.play('popup', 'popup') - elif event.detail == hippo.MOTION_DETAIL_LEAVE: - self._hover_popup = False - self._timeline.play('popdown', 'popdown') - - def _popup_action_completed_cb(self, popup): - self.popdown() - def _set_size(self, size): if size == SMALL_SIZE: self.props.box_width = -1 @@ -178,3 +92,4 @@ class IconButton(CanvasIcon): def _button_press_event_cb(self, widget, event): if self._active: self.emit_activated() + return True From c05b179675afd326ca80540cd55cfd1900e2970f Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 15:45:19 +0100 Subject: [PATCH 02/29] Fix wrong import. --- sugar/graphics/popup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sugar/graphics/popup.py b/sugar/graphics/popup.py index 93fbad87..471be308 100644 --- a/sugar/graphics/popup.py +++ b/sugar/graphics/popup.py @@ -23,7 +23,6 @@ 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 From 3ce2a6730436aaedde4f0abdebf0cd19e4766b07 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 17:05:41 +0100 Subject: [PATCH 03/29] Refactored Menu out of Popup. --- shell/view/frame/ZoomBox.py | 31 ++++---- sugar/graphics/iconbutton.py | 3 +- sugar/graphics/menu.py | 148 +++++++++++++++++------------------ sugar/graphics/popup.py | 40 +--------- 4 files changed, 92 insertions(+), 130 deletions(-) diff --git a/shell/view/frame/ZoomBox.py b/shell/view/frame/ZoomBox.py index 80463f6a..4d68a482 100644 --- a/shell/view/frame/ZoomBox.py +++ b/shell/view/frame/ZoomBox.py @@ -19,26 +19,27 @@ from gettext import gettext as _ import hippo -from sugar.graphics.popup import Popup from sugar.graphics.menuicon import MenuIcon -from sugar.graphics.menu import Menu +from sugar.graphics.menu import Menu, MenuItem from sugar.graphics.iconcolor import IconColor from sugar.graphics.iconbutton import IconButton import sugar -class ActivityPopup(Popup): +class ActivityMenu(Menu): ACTION_SHARE = 1 ACTION_CLOSE = 2 def __init__(self, activity_model): - Popup.__init__(self, activity_model.get_title()) + Menu.__init__(self, activity_model.get_title()) if not activity_model.get_shared(): - self.add_item(ActivityPopup.ACTION_SHARE, _('Share'), - 'theme:stock-share-mesh') + self.add_item(MenuItem(ActivityMenu.ACTION_SHARE, + _('Share'), + 'theme:stock-share-mesh')) - self.add_item(ActivityPopup.ACTION_CLOSE, _('Close'), - 'theme:stock-close') + self.add_item(MenuItem(ActivityMenu.ACTION_CLOSE, + _('Close'), + 'theme:stock-close')) class ActivityButton(IconButton): def __init__(self, shell, activity_model): @@ -51,16 +52,14 @@ class ActivityButton(IconButton): IconButton.__init__(self, icon_name=icon_name, color=icon_color) def get_popup(self): - popup = ActivityPopup(self._activity_model) - #popup.connect('action', self._action_cb) - return popup + menu = ActivityMenu(self._activity_model) + menu.connect('action', self._action_cb) + return menu def get_popup_context(self): return self._shell.get_popup_context() - def _action_cb(self, menu, data): - [action_id, label] = data - + def _action_cb(self, menu, menu_item): # TODO: Wouldn't be better to share/close the activity associated with # this button instead of asking for the current activity? activity = self._shell.get_current_activity() @@ -68,9 +67,9 @@ class ActivityButton(IconButton): logging.error('No active activity.') return - if action_id == ActivityPopup.ACTION_SHARE: + if menu_item.props.action_id == ActivityMenu.ACTION_SHARE: activity.share() - elif action_id == ActivityPopup.ACTION_CLOSE: + elif menu_item.props.action_id == ActivityMenu.ACTION_CLOSE: activity.close() class ZoomBox(hippo.CanvasBox): diff --git a/sugar/graphics/iconbutton.py b/sugar/graphics/iconbutton.py index 57c0712e..c2a10465 100644 --- a/sugar/graphics/iconbutton.py +++ b/sugar/graphics/iconbutton.py @@ -23,7 +23,6 @@ import hippo from canvasicon import CanvasIcon from iconcolor import IconColor from sugar.graphics import units -from sugar.graphics.timeline import Timeline from sugar import profile STANDARD_SIZE = 0 @@ -87,7 +86,7 @@ class IconButton(CanvasIcon): elif pspec.name == 'active': return self._active else: - return CanvasIcon.get_property(self, pspec) + return CanvasIcon.do_get_property(self, pspec) def _button_press_event_cb(self, widget, event): if self._active: diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index bb770669..29e342dd 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -14,94 +14,92 @@ # License along with this library; if not, write to the # Free Software Foundation, Inc., 59 Temple Place - Suite 330, # Boston, MA 02111-1307, USA. +import sys import gtk import hippo import gobject from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics.popup import Popup +from sugar.graphics.roundbox import RoundBox +from sugar.graphics import color +from sugar.graphics import font +from sugar.graphics import units -class Menu(gtk.Window): - __gsignals__ = { - 'action': (gobject.SIGNAL_RUN_FIRST, - gobject.TYPE_NONE, ([int])), +class MenuItem(hippo.CanvasBox): + __gtype_name__ = 'SugarMenuItem' + + __gproperties__ = { + 'action-id': (int, None, None, + 0, sys.maxint, 0, + gobject.PARAM_READWRITE), + 'label' : (str, None, None, None, + gobject.PARAM_READWRITE) } - def __init__(self, title=None, content_box=None): - gtk.Window.__init__(self, gtk.WINDOW_POPUP) + def __init__(self, action_id, label, icon_name=None, icon_color=None): + hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) + + self._action_id = action_id + self.props.padding = 5 + self.props.spacing = 5 - canvas = hippo.Canvas() - self.add(canvas) - canvas.show() + if icon_name: + icon = CanvasIcon(icon_name=icon_name, + scale=units.SMALL_ICON_SCALE) + if icon_color: + icon.props.color = icon_color + self.append(icon) - self._root = hippo.CanvasBox() - canvas.set_root(self._root) + self._canvas_text = hippo.CanvasText() + self._canvas_text.props.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): + Popup.__init__(self) + + self.props.background_color = color.MENU_BACKGROUND.get_int() + self.props.border_color = color.MENU_BORDER.get_int() + self.props.border = units.points_to_pixels(1) if title: - self._title_item = hippo.CanvasText(text=title) - self._root.append(self._title_item) - else: - self._title_item = None + pass - if content_box: - separator = self._create_separator() - self._root.append(separator) - self._root.append(content_box) + def add_item(self, item): + item.connect('button-press-event', self._item_button_press_event_cb) + self.append(item) - self._action_box = None - self._item_box = None + def add_separator(self): + box = hippo.CanvasBox() + box.props.background_color = color.MENU_SEPARATOR.get_int() + box.props.box_height = units.points_to_pixels(1) + self.append(box) - def _create_separator(self): - separator = hippo.CanvasBox() - return separator - - def _create_item_box(self): - if self._title_item: - separator = self._create_separator() - self._root.append(separator) - - self._item_box = hippo.CanvasBox( - orientation=hippo.ORIENTATION_VERTICAL) - self._root.append(self._item_box) - - def _create_action_box(self): - separator = self._create_separator() - self._root.append(separator) - - self._action_box = hippo.CanvasBox( - orientation=hippo.ORIENTATION_HORIZONTAL) - self._root.append(self._action_box) - - def add_item(self, label, action_id=None, wrap=False): - if not self._item_box: - self._create_item_box() - - text = hippo.CanvasText(text=label) - if wrap: - text.set_property("size-mode", "wrap-word") - - # FIXME need a way to make hippo items activable in python - if action_id: - text.connect('button-press-event', self._item_clicked_cb, action_id) - #text.connect('activated', self._action_clicked_cb, action_id) - - self._item_box.append(text) - - def add_action(self, icon, action_id): - if not self._action_box: - self._create_action_box() - - icon.connect('activated', self._action_clicked_cb, action_id) - self._action_box.append(icon) - - def remove_action(self, icon): - self._action_box.remove(icon) - - def _item_clicked_cb(self, icon, event, action): - self.emit('action', action) - - def _action_clicked_cb(self, icon, action): - self.emit('action', action) - - def set_title(self, title): - self._title_item.set_property('text', title) + def _item_button_press_event_cb(self, menu_item, event): + self.emit('action', menu_item) diff --git a/sugar/graphics/popup.py b/sugar/graphics/popup.py index 471be308..09a1fe25 100644 --- a/sugar/graphics/popup.py +++ b/sugar/graphics/popup.py @@ -21,12 +21,6 @@ import gobject import gtk import hippo -from sugar.graphics import units -from sugar.graphics.roundbox import RoundBox -from sugar.graphics import color -from sugar.graphics import font -from sugar.graphics.canvasicon import CanvasIcon - class Popup(hippo.CanvasBox, hippo.CanvasItem): __gtype_name__ = 'SugarPopup' @@ -34,38 +28,10 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem): 'action-completed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])) } - def __init__(self, title): + def __init__(self): hippo.CanvasBox.__init__(self) - self.props.background_color = color.MENU_BACKGROUND.get_int() - self.props.border_color = color.MENU_BORDER.get_int() - self.props.border = units.points_to_pixels(1) self._window = None - - def add_item(self, action_id, label, icon_name=None, icon_color=None): - box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL) - box.props.padding = 5 - box.props.spacing = 5 - if icon_name: - icon = CanvasIcon(icon_name=icon_name, - scale=units.SMALL_ICON_SCALE) - if icon_color: - icon.props.color = icon_color - box.append(icon) - - canvas_text = hippo.CanvasText() - canvas_text.props.text = label - canvas_text.props.color = color.LABEL_TEXT.get_int() - canvas_text.props.font_desc = font.DEFAULT.get_pango_desc() - box.append(canvas_text) - - box.connect('button-press-event', self._item_button_press_event_cb) - self.append(box) - - def add_separator(self): - box = hippo.CanvasBox() - box.props.background_color = color.MENU_SEPARATOR.get_int() - box.props.box_height = units.points_to_pixels(1) - self.append(box) + self.connect('button-press-event', self._button_press_event_cb) def popup(self, x, y): if not self._window: @@ -79,5 +45,5 @@ class Popup(hippo.CanvasBox, hippo.CanvasItem): self._window.destroy() self._window = None - def _item_button_press_event_cb(self, item, event): + def _button_press_event_cb(self, menu, event): self.emit('action-completed') From f5b13b716e86f18d0d9c624b9381d9d721b4c892 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 17:28:49 +0100 Subject: [PATCH 04/29] Do not expose the service names from Activity. This create quite a bit of duplication, but I have to start somewhere cleaning this mess :/ --- shell/model/homeactivity.py | 16 ++++++++++--- shell/model/homemodel.py | 11 +++++++-- shell/view/ActivityHost.py | 3 +-- sugar/activity/Activity.py | 40 ++++++++++++------------------- sugar/activity/ActivityFactory.py | 11 +++++---- 5 files changed, 45 insertions(+), 36 deletions(-) diff --git a/shell/model/homeactivity.py b/shell/model/homeactivity.py index aff4ee56..4b5a1e37 100644 --- a/shell/model/homeactivity.py +++ b/shell/model/homeactivity.py @@ -15,14 +15,18 @@ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import time -import gobject import logging +import gobject +import dbus + from sugar.graphics.iconcolor import IconColor from sugar.presence import PresenceService -from sugar.activity import Activity from sugar import profile +_ACTIVITY_SERVICE_NAME = "org.laptop.Activity" +_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" + class HomeActivity(gobject.GObject): __gsignals__ = { 'launch-timeout': (gobject.SIGNAL_RUN_FIRST, @@ -69,7 +73,10 @@ class HomeActivity(gobject.GObject): self._window = window self._xid = window.get_xid() - self._service = Activity.get_service(window.get_xid()) + + bus = dbus.SessionBus() + self._service = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % self._xid, + _ACTIVITY_SERVICE_PATH + "/%s" % self._xid) # verify id and type details act_id = self._service.get_id() @@ -79,6 +86,9 @@ class HomeActivity(gobject.GObject): if act_type != self._type: raise RuntimeError("Activity's real type (%s) didn't match expected (%s)." % (act_type, self._type)) + def get_service(self): + return self._service + def get_title(self): if not self._launched: raise RuntimeError("Activity is still launching.") diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py index 40d8bd75..bc45a0a5 100644 --- a/shell/model/homemodel.py +++ b/shell/model/homemodel.py @@ -18,10 +18,14 @@ import logging import gobject import wnck +import dbus from model.homeactivity import HomeActivity from sugar.activity import Activity +_ACTIVITY_SERVICE_NAME = "org.laptop.Activity" +_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" + class HomeModel(gobject.GObject): __gsignals__ = { @@ -114,7 +118,10 @@ class HomeModel(gobject.GObject): self.emit('active-activity-changed', self._current_activity) def _add_activity(self, window): - act_service = Activity.get_service(window.get_xid()) + bus = dbus.SessionBus() + xid = window.get_xid() + act_service = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % xid, + _ACTIVITY_SERVICE_PATH + "/%s" % xid) act_id = act_service.get_id() activity = None @@ -128,7 +135,7 @@ class HomeModel(gobject.GObject): if not bundle: raise RuntimeError("No bundle for activity type '%s'." % act_type) return - activity = HomeActivity(bundle, act_id) + activity = HomeActivity(act_service, bundle) self._activities[act_id] = activity activity.set_window(window) diff --git a/shell/view/ActivityHost.py b/shell/view/ActivityHost.py index f2ad386f..8d43bc24 100644 --- a/shell/view/ActivityHost.py +++ b/shell/view/ActivityHost.py @@ -17,7 +17,6 @@ import gtk import dbus -from sugar.activity import Activity from sugar.p2p import Stream from sugar.p2p import network from sugar.chat import ActivityChat @@ -42,7 +41,7 @@ class ActivityHost: self._model = model self._id = model.get_id() self._window = model.get_window() - self._activity = Activity.get_service(self.get_xid()) + self._activity = model.get_service() self._gdk_window = gtk.gdk.window_foreign_new(self.get_xid()) try: diff --git a/sugar/activity/Activity.py b/sugar/activity/Activity.py index 7926daed..037e09fd 100644 --- a/sugar/activity/Activity.py +++ b/sugar/activity/Activity.py @@ -30,21 +30,9 @@ 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) - +_ACTIVITY_SERVICE_NAME = "org.laptop.Activity" +_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" +_ACTIVITY_INTERFACE = "org.laptop.Activity" class ActivityDbusService(dbus.service.Object): """Base dbus service object that each Activity uses to export dbus methods. @@ -53,46 +41,48 @@ class ActivityDbusService(dbus.service.Object): tightly control what stuff passes through the dbus python bindings.""" def __init__(self, activity): - xid = activity.window.xid + service_name = _ACTIVITY_SERVICE_NAME + '%d' % activity.window.xid + object_path = _ACTIVITY_SERVICE_PATH + "/%s" % 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)) + bus_name = dbus.service.BusName(service_name, bus=bus) + dbus.service.Object.__init__(self, bus_name, object_path) self._activity = activity self._pservice = PresenceService.get_instance() - @dbus.service.method(ACTIVITY_INTERFACE) + @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) + @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) + @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) + @dbus.service.method(_ACTIVITY_INTERFACE) def get_id(self): """Get the activity identifier""" return self._activity.get_id() - @dbus.service.method(ACTIVITY_INTERFACE) + @dbus.service.method(_ACTIVITY_INTERFACE) def get_type(self): """Get the activity type""" return self._activity.get_type() - @dbus.service.method(ACTIVITY_INTERFACE) + @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, + @dbus.service.method(_ACTIVITY_INTERFACE, in_signature="sas", out_signature="b") def execute(self, command, args): return self._activity.execute(command, args) diff --git a/sugar/activity/ActivityFactory.py b/sugar/activity/ActivityFactory.py index 94e765ec..8ecb4c8d 100644 --- a/sugar/activity/ActivityFactory.py +++ b/sugar/activity/ActivityFactory.py @@ -25,10 +25,13 @@ 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 +_ACTIVITY_SERVICE_NAME = "org.laptop.Activity" +_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" +_ACTIVITY_INTERFACE = "org.laptop.Activity" + def get_path(activity_name): """Returns the activity path""" return '/' + activity_name.replace('.', '/') @@ -101,9 +104,9 @@ class ActivityCreationHandler(gobject.GObject): 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) + proxy_obj = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % xid, + _ACTIVITY_SERVICE_PATH + "/%s" % xid) + activity = dbus.Interface(proxy_obj, _ACTIVITY_INTERFACE) self.emit('success', activity) def _error_handler(self, err): From 7db372cc1cdc2d2c19af83c9566b14fa406a73a2 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 17:53:44 +0100 Subject: [PATCH 05/29] Make bundle registry a singleton. Get the object path from the registry. --- shell/model/MeshModel.py | 5 +++-- shell/model/ShellModel.py | 15 ++------------- shell/model/homemodel.py | 6 +++--- shell/view/frame/ActivitiesBox.py | 3 ++- sugar/activity/ActivityFactory.py | 15 ++++++++------- sugar/activity/bundle.py | 4 ++++ sugar/activity/bundleregistry.py | 11 +++++++++++ 7 files changed, 33 insertions(+), 26 deletions(-) diff --git a/shell/model/MeshModel.py b/shell/model/MeshModel.py index a2611565..6c9f8832 100644 --- a/shell/model/MeshModel.py +++ b/shell/model/MeshModel.py @@ -18,6 +18,7 @@ import gobject from sugar.graphics.iconcolor import IconColor from sugar.presence import PresenceService +from sugar.activity import bundleregistry from model.BuddyModel import BuddyModel class ActivityModel: @@ -53,12 +54,12 @@ class MeshModel(gobject.GObject): gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])) } - def __init__(self, bundle_registry): + def __init__(self): gobject.GObject.__init__(self) self._activities = {} self._buddies = {} - self._bundle_registry = bundle_registry + self._bundle_registry = bundleregistry.get_registry() self._pservice = PresenceService.get_instance() self._pservice.connect("service-appeared", diff --git a/shell/model/ShellModel.py b/shell/model/ShellModel.py index abfff21c..4c0df121 100644 --- a/shell/model/ShellModel.py +++ b/shell/model/ShellModel.py @@ -44,8 +44,6 @@ class ShellModel(gobject.GObject): self._current_activity = None self._state = self.STATE_RUNNING - self._bundle_registry = BundleRegistry() - PresenceService.start() self._pservice = PresenceService.get_instance() @@ -53,16 +51,10 @@ class ShellModel(gobject.GObject): self._owner.announce() self._friends = Friends() - self._mesh = MeshModel(self._bundle_registry) - self._home = HomeModel(self._bundle_registry) + self._mesh = MeshModel() + self._home = HomeModel() self._devices = DevicesModel() - for path in env.get_data_dirs(): - bundles_path = os.path.join(path, 'activities') - self._bundle_registry.add_search_path(bundles_path) - - self._bundle_registry.add_search_path(env.get_user_activities_dir()) - def do_set_property(self, pspec, value): if pspec.name == 'state': self._state = value @@ -71,9 +63,6 @@ class ShellModel(gobject.GObject): if pspec.name == 'state': return self._state - def get_bundle_registry(self): - return self._bundle_registry - def get_mesh(self): return self._mesh diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py index bc45a0a5..ff04dfcd 100644 --- a/shell/model/homemodel.py +++ b/shell/model/homemodel.py @@ -21,7 +21,7 @@ import wnck import dbus from model.homeactivity import HomeActivity -from sugar.activity import Activity +from sugar.activity import bundleregistry _ACTIVITY_SERVICE_NAME = "org.laptop.Activity" _ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" @@ -43,11 +43,11 @@ class HomeModel(gobject.GObject): ([gobject.TYPE_PYOBJECT])) } - def __init__(self, bundle_registry): + def __init__(self): gobject.GObject.__init__(self) self._activities = {} - self._bundle_registry = bundle_registry + self._bundle_registry = bundleregistry.get_registry() self._current_activity = None screen = wnck.screen_get_default() diff --git a/shell/view/frame/ActivitiesBox.py b/shell/view/frame/ActivitiesBox.py index 196309d1..ea64b7f3 100644 --- a/shell/view/frame/ActivitiesBox.py +++ b/shell/view/frame/ActivitiesBox.py @@ -21,6 +21,7 @@ from sugar.graphics import units from sugar.graphics.iconcolor import IconColor from sugar.graphics.iconbutton import IconButton from sugar.presence import PresenceService +from sugar.activity import bundleregistry from sugar import profile class ActivityButton(IconButton): @@ -63,7 +64,7 @@ class ActivitiesBox(hippo.CanvasBox): self._invite_to_item = {} self._invites = self._shell_model.get_invites() - bundle_registry = self._shell_model.get_bundle_registry() + bundle_registry = bundleregistry.get_registry() for bundle in bundle_registry: if bundle.get_show_launcher(): self.add_activity(bundle) diff --git a/sugar/activity/ActivityFactory.py b/sugar/activity/ActivityFactory.py index 8ecb4c8d..ec45fbe2 100644 --- a/sugar/activity/ActivityFactory.py +++ b/sugar/activity/ActivityFactory.py @@ -25,6 +25,7 @@ import gobject import gtk from sugar.presence.PresenceService import PresenceService +from sugar.activity import bundleregistry from sugar.activity.bundle import Bundle from sugar import logger @@ -90,14 +91,14 @@ class ActivityCreationHandler(gobject.GObject): ([gobject.TYPE_PYOBJECT])) } - def __init__(self, activity_name): + def __init__(self, service_name): gobject.GObject.__init__(self) - bus = dbus.SessionBus() - factory_name = activity_name - factory_path = get_path(factory_name) + registry = bundleregistry.get_registry() + bundle = registry.get_bundle(service_name) - proxy_obj = bus.get_object(factory_name, factory_path) + 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(reply_handler=self._reply_handler, error_handler=self._error_handler) @@ -113,9 +114,9 @@ class ActivityCreationHandler(gobject.GObject): logging.debug("Couldn't create activity: %s" % err) self.emit('error', err) -def create(activity_name): +def create(service_name): """Create a new activity from its name.""" - return ActivityCreationHandler(activity_name) + return ActivityCreationHandler(service_name) def start_factory(activity_class, bundle_path): """Start the activity factory.""" diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index c7675de0..8c17958a 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -71,6 +71,10 @@ class Bundle: """Get the activity service name""" return self._service_name + def get_object_path(self): + """Get the path to the service object""" + return '/' + self._service_name.replace('.', '/') + def get_default_type(self): """Get the type of the main network service which tracks presence and provides info about the activity, for example the title.""" diff --git a/sugar/activity/bundleregistry.py b/sugar/activity/bundleregistry.py index 22a84e1c..ccf3b794 100644 --- a/sugar/activity/bundleregistry.py +++ b/sugar/activity/bundleregistry.py @@ -73,3 +73,14 @@ class BundleRegistry(gobject.GObject): return True else: return False + +def get_registry(): + return _bundle_registry + +_bundle_registry = BundleRegistry() + +for path in env.get_data_dirs(): + bundles_path = os.path.join(path, 'activities') + _bundle_registry.add_search_path(bundles_path) + +_bundle_registry.add_search_path(env.get_user_activities_dir()) From 7be3333671d537e02554a28e71b074f3b87d5e88 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 17:55:44 +0100 Subject: [PATCH 06/29] Make BuddyIcon and BuddyMenu use the new Menu. --- shell/view/BuddyIcon.py | 17 ++++++++++------- shell/view/BuddyMenu.py | 35 +++++++++++++++++++---------------- sugar/graphics/menu.py | 9 ++++++--- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/shell/view/BuddyIcon.py b/shell/view/BuddyIcon.py index 8e910edc..5ca9e8be 100644 --- a/shell/view/BuddyIcon.py +++ b/shell/view/BuddyIcon.py @@ -14,13 +14,13 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -from sugar.graphics.menuicon import MenuIcon +from sugar.graphics.canvasicon import CanvasIcon from view.BuddyMenu import BuddyMenu -class BuddyIcon(MenuIcon): +class BuddyIcon(CanvasIcon): def __init__(self, shell, menu_shell, buddy): - MenuIcon.__init__(self, menu_shell, icon_name='theme:stock-buddy', - color=buddy.get_color()) + CanvasIcon.__init__(self, icon_name='theme:stock-buddy', + color=buddy.get_color()) self._shell = shell self._buddy = buddy @@ -35,13 +35,16 @@ class BuddyIcon(MenuIcon): def set_popup_distance(self, distance): self._popup_distance = distance - def create_menu(self): + def get_popup(self): menu = BuddyMenu(self._shell, self._buddy) menu.connect('action', self._popup_action_cb) return menu - def _popup_action_cb(self, popup, action): - self.popdown() + def get_popup_context(self): + return self._shell.get_popup_context() + + def _popup_action_cb(self, popup, menu_item): + action = menu_item.props.action_id friends = self._shell.get_model().get_friends() if action == BuddyMenu.ACTION_REMOVE_FRIEND: diff --git a/shell/view/BuddyMenu.py b/shell/view/BuddyMenu.py index 2909fd91..b4548224 100644 --- a/shell/view/BuddyMenu.py +++ b/shell/view/BuddyMenu.py @@ -13,18 +13,18 @@ # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +from gettext import gettext as _ import gtk import gobject import hippo -from sugar.graphics.menu import Menu +from sugar.graphics.menu import Menu, MenuItem from sugar.graphics.canvasicon import CanvasIcon +from sugar.graphics import units from sugar.presence import PresenceService import _sugar -_ICON_SIZE = 75 - class BuddyMenu(Menu): ACTION_MAKE_FRIEND = 0 ACTION_INVITE = 1 @@ -34,25 +34,26 @@ class BuddyMenu(Menu): self._buddy = buddy self._shell = shell + Menu.__init__(self, buddy.get_name()) pixbuf = self._get_buddy_icon_pixbuf() if pixbuf: icon_item = hippo.CanvasImage() - scaled_pixbuf = pixbuf.scale_simple(_ICON_SIZE, _ICON_SIZE, + scaled_pixbuf = pixbuf.scale_simple(units.grid_to_pixels(1), + units.grid_to_pixels(1), gtk.gdk.INTERP_BILINEAR) del pixbuf - Menu.__init__(self, buddy.get_name(), icon_item) + self.add_separator() + self.append(icon_item) # FIXME: have to set the image _after_ adding the HippoCanvasImage # to it's parent item, because that sets the HippoCanvasImage's context, # which resets the object's 'image' property. Grr. _sugar.hippo_canvas_image_set_image_from_gdk_pixbuf(icon_item, scaled_pixbuf) - else: - Menu.__init__(self, buddy.get_name(), None) self._buddy.connect('icon-changed', self.__buddy_icon_changed_cb) owner = shell.get_model().get_owner() if buddy.get_name() != owner.get_name(): - self._add_actions() + self._add_items() def _get_buddy_icon_pixbuf(self): buddy_object = self._buddy.get_buddy() @@ -76,17 +77,19 @@ class BuddyMenu(Menu): del pbl return pixbuf - def _add_actions(self): + def _add_items(self): shell_model = self._shell.get_model() pservice = PresenceService.get_instance() friends = shell_model.get_friends() if friends.has_buddy(self._buddy): - icon = CanvasIcon(icon_name='theme:stock-remove') - self.add_action(icon, BuddyMenu.ACTION_REMOVE_FRIEND) + self.add_item(MenuItem(BuddyMenu.ACTION_REMOVE_FRIEND, + _('Remove friend'), + 'theme:stock-remove')) else: - icon = CanvasIcon(icon_name='theme:stock-add') - self.add_action(icon, BuddyMenu.ACTION_MAKE_FRIEND) + self.add_item(MenuItem(BuddyMenu.ACTION_MAKE_FRIEND, + _('Make friend'), + 'theme:stock-add')) activity = shell_model.get_home().get_current_activity() if activity != None: @@ -94,9 +97,9 @@ class BuddyMenu(Menu): # FIXME check that the buddy is not in the activity already - icon = CanvasIcon(icon_name='theme:stock-invite') - self.add_action(icon, BuddyMenu.ACTION_INVITE) + self.add_item(MenuItem(BuddyMenu.ACTION_INVITE, + _('Invite'), + 'theme:stock-invite')) def __buddy_icon_changed_cb(self, buddy): pass - diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index 29e342dd..e32eabe9 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -52,8 +52,7 @@ class MenuItem(hippo.CanvasBox): icon.props.color = icon_color self.append(icon) - self._canvas_text = hippo.CanvasText() - self._canvas_text.props.text = label + 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) @@ -89,7 +88,11 @@ class Menu(Popup): self.props.border = units.points_to_pixels(1) if title: - pass + title_item = hippo.CanvasText(text=title) + title_item.props.color = color.LABEL_TEXT.get_int() + title_item.props.font_desc = font.DEFAULT.get_pango_desc() + self.append(title_item) + self.add_separator() def add_item(self, item): item.connect('button-press-event', self._item_button_press_event_cb) From 11264b48b2c1072b96b86f26e079e6a29a3dcf84 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 18:06:39 +0100 Subject: [PATCH 07/29] Split factory client and server --- shell/sugar-activity-factory | 4 +- sugar/activity/ActivityFactory.py | 64 ------------------- sugar/activity/Makefile.am | 13 ++-- sugar/activity/activityfactoryservice.py | 78 ++++++++++++++++++++++++ 4 files changed, 87 insertions(+), 72 deletions(-) create mode 100644 sugar/activity/activityfactoryservice.py diff --git a/shell/sugar-activity-factory b/shell/sugar-activity-factory index 5ef06278..588d2d5b 100755 --- a/shell/sugar-activity-factory +++ b/shell/sugar-activity-factory @@ -29,10 +29,10 @@ import dbus.glib gobject.threads_init() dbus.glib.threads_init() -from sugar.activity import ActivityFactory +from sugar.activity import activityfactoryservice sys.path.insert(0, sys.argv[2]) -ActivityFactory.start_factory(sys.argv[1], sys.argv[2]) +activityfactoryservice.start(sys.argv[1], sys.argv[2]) gtk.main() diff --git a/sugar/activity/ActivityFactory.py b/sugar/activity/ActivityFactory.py index ec45fbe2..ef27f48d 100644 --- a/sugar/activity/ActivityFactory.py +++ b/sugar/activity/ActivityFactory.py @@ -15,71 +15,19 @@ # 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 bundleregistry -from sugar.activity.bundle import Bundle -from sugar import logger _ACTIVITY_SERVICE_NAME = "org.laptop.Activity" _ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" _ACTIVITY_INTERFACE = "org.laptop.Activity" -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__ = { @@ -117,15 +65,3 @@ class ActivityCreationHandler(gobject.GObject): def create(service_name): """Create a new activity from its name.""" return ActivityCreationHandler(service_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) diff --git a/sugar/activity/Makefile.am b/sugar/activity/Makefile.am index 12b09075..fb1066c4 100644 --- a/sugar/activity/Makefile.am +++ b/sugar/activity/Makefile.am @@ -1,8 +1,9 @@ sugardir = $(pythondir)/sugar/activity -sugar_PYTHON = \ - __init__.py \ - Activity.py \ - ActivityFactory.py \ - bundle.py \ - bundlebuilder.py \ +sugar_PYTHON = \ + __init__.py \ + Activity.py \ + ActivityFactory.py \ + activityfactoryservice.py \ + bundle.py \ + bundlebuilder.py \ bundleregistry.py diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py new file mode 100644 index 00000000..8ae985e4 --- /dev/null +++ b/sugar/activity/activityfactoryservice.py @@ -0,0 +1,78 @@ +# 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 dbus +import dbus.service + +from sugar.activity.bundle import Bundle +from sugar import logger + +class ActivityFactory(dbus.service.Object): + """Dbus service that takes care of creating 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") + 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() + +def start(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) From 3d3c730f22225eec2677cad9c820b0f3283378b3 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 18:09:02 +0100 Subject: [PATCH 08/29] Rename the class --- sugar/activity/activityfactoryservice.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py index 8ae985e4..15ebf144 100644 --- a/sugar/activity/activityfactoryservice.py +++ b/sugar/activity/activityfactoryservice.py @@ -23,7 +23,7 @@ import dbus.service from sugar.activity.bundle import Bundle from sugar import logger -class ActivityFactory(dbus.service.Object): +class ActivityFactoryService(dbus.service.Object): """Dbus service that takes care of creating new instances of an activity""" def __init__(self, service_name, activity_class): @@ -75,4 +75,5 @@ def start(activity_class, 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) + factory = ActivityFactoryService(bundle.get_service_name(), + activity_class) From 3fa653ed63a74311015a76ca83e43aec9130745f Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 19:39:11 +0100 Subject: [PATCH 09/29] Split out Activity client/server --- sugar/activity/activityservice.py | 79 +++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 sugar/activity/activityservice.py diff --git a/sugar/activity/activityservice.py b/sugar/activity/activityservice.py new file mode 100644 index 00000000..5c1654aa --- /dev/null +++ b/sugar/activity/activityservice.py @@ -0,0 +1,79 @@ +# 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 + 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) From 70da5fa7bd4000dbc2d7dbfaf6637372d1448c32 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 20:15:39 +0100 Subject: [PATCH 10/29] More cleanups --- sugar/activity/Activity.py | 90 +------------------------------------- sugar/activity/Makefile.am | 1 + 2 files changed, 3 insertions(+), 88 deletions(-) diff --git a/sugar/activity/Activity.py b/sugar/activity/Activity.py index 037e09fd..e34b73e9 100644 --- a/sugar/activity/Activity.py +++ b/sugar/activity/Activity.py @@ -15,77 +15,13 @@ # 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.activity.activityservice import ActivityService from sugar import env -import sugar.util - -_ACTIVITY_SERVICE_NAME = "org.laptop.Activity" -_ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" -_ACTIVITY_INTERFACE = "org.laptop.Activity" - -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): - service_name = _ACTIVITY_SERVICE_NAME + '%d' % activity.window.xid - object_path = _ACTIVITY_SERVICE_PATH + "/%s" % activity.window.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 - 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.""" @@ -94,12 +30,10 @@ class Activity(gtk.Window): 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() @@ -108,7 +42,7 @@ class Activity(gtk.Window): group.realize() self.window.set_group(group.window) - self._bus = ActivityDbusService(self) + self._bus = ActivityService(self) def start(self, activity_id): """Start the activity.""" @@ -118,19 +52,8 @@ class Activity(gtk.Window): 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() @@ -170,9 +93,6 @@ class Activity(gtk.Window): 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): @@ -193,9 +113,3 @@ class Activity(gtk.Window): 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}) diff --git a/sugar/activity/Makefile.am b/sugar/activity/Makefile.am index fb1066c4..0ef3d788 100644 --- a/sugar/activity/Makefile.am +++ b/sugar/activity/Makefile.am @@ -4,6 +4,7 @@ sugar_PYTHON = \ Activity.py \ ActivityFactory.py \ activityfactoryservice.py \ + activityservice.py \ bundle.py \ bundlebuilder.py \ bundleregistry.py From ef3f86195449b8a8a480e8905b3996eb936cdd8b Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 20:20:36 +0100 Subject: [PATCH 11/29] Fix caps --- shell/sugar-activity | 4 ++-- shell/view/Shell.py | 6 +++--- shell/view/clipboardicon.py | 4 ++-- sugar/activity/Makefile.am | 2 +- sugar/activity/{ActivityFactory.py => activityfactory.py} | 0 5 files changed, 8 insertions(+), 8 deletions(-) rename sugar/activity/{ActivityFactory.py => activityfactory.py} (100%) diff --git a/shell/sugar-activity b/shell/sugar-activity index 3f3c93fb..6f5c9741 100755 --- a/shell/sugar-activity +++ b/shell/sugar-activity @@ -20,7 +20,7 @@ import sys import os import gobject -from sugar.activity import ActivityFactory +from sugar.activity import activityfactory from sugar import env from sugar import util @@ -40,7 +40,7 @@ os.environ['DBUS_SESSION_BUS_ADDRESS'] = bus_name loop = gobject.MainLoop() -handler = ActivityFactory.create(sys.argv[1]) +handler = activityfactory.create(sys.argv[1]) handler.connect('success', _success_cb, loop) handler.connect('error', _error_cb, loop) diff --git a/shell/view/Shell.py b/shell/view/Shell.py index 47fe02c3..45db412a 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -23,7 +23,7 @@ from view.home.HomeWindow import HomeWindow from sugar.presence import PresenceService from sugar.graphics.popupcontext import PopupContext from view.ActivityHost import ActivityHost -from sugar.activity import ActivityFactory +from sugar.activity import activityfactory from view.frame.frame import Frame from view.keyhandler import KeyHandler from view.hardwaremanager import HardwareManager @@ -137,7 +137,7 @@ class Shell(gobject.GObject): home_model = self._model.get_home() home_model.notify_activity_launch(activity_id, act_type) - handler = ActivityFactory.create(act_type) + handler = activityfactory.create(act_type) handler.connect('success', self._join_success_cb, activity_ps, activity_id, act_type) handler.connect('error', self._join_error_cb, home_model, activity_id, act_type) @@ -190,7 +190,7 @@ class Shell(gobject.GObject): home_model.notify_activity_launch(act_id, activity_type) logging.debug("Shell.start_activity will start %s (%s)" % (act_id, activity_type)) - handler = ActivityFactory.create(activity_type) + handler = activityfactory.create(activity_type) handler.connect('success', self._start_success_cb, act_id, activity_type) handler.connect('error', self._start_error_cb, home_model, act_id, activity_type) diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index ad74c1b5..f0c609b8 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -3,7 +3,7 @@ import logging from sugar.graphics.menuicon import MenuIcon from view.clipboardmenu import ClipboardMenu from sugar.graphics.iconcolor import IconColor -from sugar.activity import ActivityFactory +from sugar.activity import activityfactory from sugar.clipboard import clipboardservice from sugar import util @@ -53,7 +53,7 @@ class ClipboardIcon(MenuIcon): logging.debug("_icon_activated_cb: " + self._object_id) # Launch the activity to handle this item - handler = ActivityFactory.create(self._activity) + handler = activityfactory.create(self._activity) handler.connect('success', self._activity_create_success_cb) handler.connect('error', self._activity_create_error_cb) diff --git a/sugar/activity/Makefile.am b/sugar/activity/Makefile.am index 0ef3d788..df6af5cb 100644 --- a/sugar/activity/Makefile.am +++ b/sugar/activity/Makefile.am @@ -2,7 +2,7 @@ sugardir = $(pythondir)/sugar/activity sugar_PYTHON = \ __init__.py \ Activity.py \ - ActivityFactory.py \ + activityfactory.py \ activityfactoryservice.py \ activityservice.py \ bundle.py \ diff --git a/sugar/activity/ActivityFactory.py b/sugar/activity/activityfactory.py similarity index 100% rename from sugar/activity/ActivityFactory.py rename to sugar/activity/activityfactory.py From 9ba487fa1f5ea1e624eb84be225940096d0185cb Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Wed, 21 Feb 2007 20:56:14 +0100 Subject: [PATCH 12/29] Generate the id in ActivityFactory. I want it to be easy to run activities without using the shell. Some regression in the debug messages, which I'll fix as part of further refactoring. --- shell/view/Shell.py | 65 +++++++------------------------ sugar/activity/activityfactory.py | 33 +++++++++++++++- 2 files changed, 46 insertions(+), 52 deletions(-) diff --git a/shell/view/Shell.py b/shell/view/Shell.py index 45db412a..232652a2 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -105,13 +105,11 @@ class Shell(gobject.GObject): def get_popup_context(self): return self._popup_context - def _join_success_cb(self, handler, activity, activity_ps, activity_id, activity_type): - logging.debug("Joining activity %s (%s)" % (activity_id, activity_type)) + def _join_success_cb(self, handler, activity, activity_ps): activity.join(activity_ps.object_path()) - def _join_error_cb(self, handler, err, home_model, activity_id, activity_type): - logging.error("Couldn't launch activity %s (%s):\n%s" % (activity_id, activity_type, err)) - home_mode.notify_activity_launch_failed(activity_id) + def _join_error_cb(self, handler, err, home_model): + home_mode.notify_activity_launch_failed(handler.get_activity_id()) def join_activity(self, bundle_id, activity_id): activity = self.get_activity(activity_id) @@ -138,61 +136,26 @@ class Shell(gobject.GObject): home_model.notify_activity_launch(activity_id, act_type) handler = activityfactory.create(act_type) - handler.connect('success', self._join_success_cb, activity_ps, activity_id, act_type) - handler.connect('error', self._join_error_cb, home_model, activity_id, act_type) + handler.connect('success', self._join_success_cb, activity_ps) + handler.connect('error', self._join_error_cb, home_model) - def _find_unique_activity_id(self): - # create a new unique activity ID - i = 0 - act_id = None - while i < 10: - act_id = sugar.util.unique_id() - i += 1 - - # check through existing activities - found = False - for xid, act_host in self._hosts.items(): - if act_host.get_id() == act_id: - found = True - break - if found: - act_id = None - continue - - # check through network activities - activities = self._pservice.get_activities() - for act in activities: - if act_id == act.get_id(): - found = True - break - if found: - act_id = None - continue - - return act_id - - def _start_success_cb(self, handler, activity, activity_id, activity_type): - logging.debug("Started activity %s (%s)" % (activity_id, activity_type)) - activity.start(activity_id) + def _start_success_cb(self, handler, activity): + activity.start(handler.get_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) + home_model.notify_activity_launch_failed(handler.get_activity_id()) def start_activity(self, activity_type): logging.debug('Shell.start_activity') - act_id = self._find_unique_activity_id() - if not act_id: - logging.error("Couldn't find available activity ID.") - return None + + handler = activityfactory.create(activity_type) home_model = self._model.get_home() - home_model.notify_activity_launch(act_id, activity_type) + home_model.notify_activity_launch(handler.get_activity_id(), + activity_type) - logging.debug("Shell.start_activity will start %s (%s)" % (act_id, activity_type)) - handler = activityfactory.create(activity_type) - handler.connect('success', self._start_success_cb, act_id, activity_type) - handler.connect('error', self._start_error_cb, home_model, act_id, activity_type) + handler.connect('success', self._start_success_cb) + handler.connect('error', self._start_error_cb, home_model) # Zoom to Home for launch feedback self.set_zoom_level(sugar.ZOOM_HOME) diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index ef27f48d..e2a0d4d2 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -21,8 +21,9 @@ import dbus import gobject import gtk -from sugar.presence.PresenceService import PresenceService +from sugar.presence import PresenceService from sugar.activity import bundleregistry +from sugar import util _ACTIVITY_SERVICE_NAME = "org.laptop.Activity" _ACTIVITY_SERVICE_PATH = "/org/laptop/Activity" @@ -42,6 +43,10 @@ class ActivityCreationHandler(gobject.GObject): def __init__(self, service_name): gobject.GObject.__init__(self) + self._activity_id = self._find_unique_activity_id() + if not self._activity_id: + raise RuntimeError("Cannot generate activity id.") + registry = bundleregistry.get_registry() bundle = registry.get_bundle(service_name) @@ -51,6 +56,32 @@ class ActivityCreationHandler(gobject.GObject): factory.create(reply_handler=self._reply_handler, error_handler=self._error_handler) + def get_activity_id(self): + return self._activity_id + + def _find_unique_activity_id(self): + 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: + act_id = None + continue + + return act_id + def _reply_handler(self, xid): bus = dbus.SessionBus() proxy_obj = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % xid, From b452b7d718f06703c682e935d1bf6d28a425d108 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 21:12:27 +0100 Subject: [PATCH 13/29] Make ClipboardIcon use the new Menu. --- shell/view/clipboardicon.py | 18 +++-- shell/view/clipboardmenu.py | 93 ++++++++++++++---------- shell/view/frame/Makefile.am | 3 +- shell/view/frame/PanelWindow.py | 6 -- shell/view/frame/ZoomBox.py | 11 +-- shell/view/frame/clipboardbox.py | 6 +- shell/view/frame/clipboardpanelwindow.py | 3 +- shell/view/frame/frame.py | 46 ++++-------- shell/view/frame/framepopupcontext.py | 26 +++++++ sugar/graphics/menu.py | 3 + 10 files changed, 121 insertions(+), 94 deletions(-) create mode 100644 shell/view/frame/framepopupcontext.py diff --git a/shell/view/clipboardicon.py b/shell/view/clipboardicon.py index f0c609b8..48e8bebb 100644 --- a/shell/view/clipboardicon.py +++ b/shell/view/clipboardicon.py @@ -1,16 +1,17 @@ import logging -from sugar.graphics.menuicon import MenuIcon +from sugar.graphics.canvasicon import CanvasIcon from view.clipboardmenu import ClipboardMenu from sugar.graphics.iconcolor import IconColor from sugar.activity import activityfactory from sugar.clipboard import clipboardservice from sugar import util -class ClipboardIcon(MenuIcon): +class ClipboardIcon(CanvasIcon): - def __init__(self, menu_shell, object_id, name): - MenuIcon.__init__(self, menu_shell) + def __init__(self, popup_context, object_id, name): + CanvasIcon.__init__(self) + self._popup_context = popup_context self._object_id = object_id self._name = name self._percent = 0 @@ -19,12 +20,15 @@ class ClipboardIcon(MenuIcon): self.connect('activated', self._icon_activated_cb) self._menu = None - def create_menu(self): + def get_popup(self): self._menu = ClipboardMenu(self._name, self._percent, self._preview, self._activity) self._menu.connect('action', self._popup_action_cb) return self._menu + def get_popup_context(self): + return self._popup_context + def set_state(self, name, percent, icon_name, preview, activity): self._name = name self._percent = percent @@ -60,8 +64,8 @@ class ClipboardIcon(MenuIcon): def _icon_activated_cb(self, icon): self._open_file() - def _popup_action_cb(self, popup, action): - self.popdown() + def _popup_action_cb(self, popup, menu_item): + action = menu_item.props.action_id if action == ClipboardMenu.ACTION_STOP_DOWNLOAD: raise "Stopping downloads still not implemented." diff --git a/shell/view/clipboardmenu.py b/shell/view/clipboardmenu.py index 77e50387..1ea53616 100644 --- a/shell/view/clipboardmenu.py +++ b/shell/view/clipboardmenu.py @@ -1,16 +1,23 @@ +from gettext import gettext as _ + import hippo -from sugar.graphics.menu import Menu +from sugar.graphics.menu import Menu, MenuItem from sugar.graphics.canvasicon import CanvasIcon from sugar.graphics.ClipboardBubble import ClipboardBubble +from sugar.graphics import color +from sugar.graphics import font -class ClipboardMenuItem(ClipboardBubble): +class ClipboardProgressBar(ClipboardBubble): def __init__(self, percent = 0): self._text_item = None ClipboardBubble.__init__(self, percent=percent) self._text_item = hippo.CanvasText(text=str(percent) + ' %') + self._text_item.props.color = color.LABEL_TEXT.get_int() + self._text_item.props.font_desc = font.DEFAULT.get_pango_desc() + self.append(self._text_item) def do_set_property(self, pspec, value): @@ -30,57 +37,69 @@ class ClipboardMenu(Menu): Menu.__init__(self, name) if percent < 100: - self._progress_bar = ClipboardMenuItem(percent) - self._root.append(self._progress_bar) + self._progress_bar = ClipboardProgressBar(percent) + self.append(self._progress_bar) else: self._progress_bar = None - self._remove_icon = None - self._open_icon = None - self._stop_icon = None - - self.add_item(preview, wrap=True) + self._remove_item = None + self._open_item = None + self._stop_item = None + + if preview: + self._preview_text = hippo.CanvasText(text=preview, + size_mode=hippo.CANVAS_SIZE_WRAP_WORD) + self._preview_text.props.color = color.LABEL_TEXT.get_int() + self._preview_text.props.font_desc = font.DEFAULT.get_pango_desc() + self.append(self._preview_text) self._update_icons(percent, activity) def _update_icons(self, percent, activity): - if percent == 100 and activity: - if not self._remove_icon: - self._remove_icon = CanvasIcon(icon_name='theme:stock-remove') - self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE) + if not self._remove_item: + self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, + _('Remove'), + 'theme:stock-remove') + self.add_item(self._remove_item) - if not self._open_icon: - self._open_icon = CanvasIcon(icon_name='theme:stock-keep') - self.add_action(self._open_icon, ClipboardMenu.ACTION_OPEN) + if not self._open_item: + self._open_item = MenuItem(ClipboardMenu.ACTION_OPEN, + _('Open'), + 'theme:stock-keep') + self.add_item(self._open_item) - if self._stop_icon: - self.remove_action(self._stop_icon) - self._stop_icon = None + if self._stop_item: + self.remove_item(self._stop_item) + self._stop_item = None elif percent == 100 and not activity: - if not self._remove_icon: - self._remove_icon = CanvasIcon(icon_name='theme:stock-remove') - self.add_action(self._remove_icon, ClipboardMenu.ACTION_DELETE) + if not self._remove_item: + self._remove_item = MenuItem(ClipboardMenu.ACTION_DELETE, + _('Remove'), + 'theme:stock-remove') + self.add_item(self._remove_item) - if self._open_icon: - self.remove_action(self._open_icon) - self._open_icon = None + if self._open_item: + self.remove_item(self._open_item) + self._open_item = None - if self._stop_icon: - self.remove_action(self._stop_icon) - self._stop_icon = None + if self._stop_item: + self.remove_item(self._stop_item) + self._stop_item = None else: - if not self._stop_icon: - self._stop_icon = CanvasIcon(icon_name='theme:stock-close') - self.add_action(self._stop_icon, ClipboardMenu.ACTION_STOP_DOWNLOAD) + if not self._stop_item: + self._stop_item = MenuItem(ClipboardMenu.ACTION_STOP_DOWNLOAD, + _('Stop download'), + 'theme:stock-close') + self.add_item(self._stop_item) - if self._remove_icon: - self.remove_action(self._remove_icon) - self._remove_icon = None + if self._remove_item: + self.remove_item(self._remove_item) + self._remove_item = None - if self._open_icon: - self.remove_action(self._open_icon) - self._open_icon = None + if self._open_item: + self.remove_item(self._open_item) + self._open_item = None def set_state(self, name, percent, preview, activity): self.set_title(name) diff --git a/shell/view/frame/Makefile.am b/shell/view/frame/Makefile.am index 1aaabd6b..c4e6dc22 100644 --- a/shell/view/frame/Makefile.am +++ b/shell/view/frame/Makefile.am @@ -10,4 +10,5 @@ sugar_PYTHON = \ ZoomBox.py \ notificationtray.py \ overlaybox.py \ - PanelWindow.py + PanelWindow.py \ + framepopupcontext.py diff --git a/shell/view/frame/PanelWindow.py b/shell/view/frame/PanelWindow.py index 24f268d8..e14f6b4b 100644 --- a/shell/view/frame/PanelWindow.py +++ b/shell/view/frame/PanelWindow.py @@ -17,7 +17,6 @@ import gtk import hippo -from sugar.graphics.menushell import MenuShell from sugar.graphics import units class PanelWindow(gtk.Window): @@ -51,13 +50,8 @@ class PanelWindow(gtk.Window): self.add(self._canvas) self._canvas.show() - self._menu_shell = MenuShell(self._canvas) - self.resize(width, height) - def get_menu_shell(self): - return self._menu_shell - def get_root(self): return self._bg diff --git a/shell/view/frame/ZoomBox.py b/shell/view/frame/ZoomBox.py index 4d68a482..57bc78a5 100644 --- a/shell/view/frame/ZoomBox.py +++ b/shell/view/frame/ZoomBox.py @@ -42,9 +42,10 @@ class ActivityMenu(Menu): 'theme:stock-close')) class ActivityButton(IconButton): - def __init__(self, shell, activity_model): + def __init__(self, shell, activity_model, popup_context): self._shell = shell self._activity_model = activity_model + self._popup_context = popup_context icon_name = self._activity_model.get_icon_name() icon_color = self._activity_model.get_icon_color() @@ -57,7 +58,7 @@ class ActivityButton(IconButton): return menu def get_popup_context(self): - return self._shell.get_popup_context() + return self._popup_context def _action_cb(self, menu, menu_item): # TODO: Wouldn't be better to share/close the activity associated with @@ -73,11 +74,11 @@ class ActivityButton(IconButton): activity.close() class ZoomBox(hippo.CanvasBox): - def __init__(self, shell, menu_shell): + def __init__(self, shell, popup_context): hippo.CanvasBox.__init__(self, orientation=hippo.ORIENTATION_HORIZONTAL) self._shell = shell - self._menu_shell = menu_shell + self._popup_context = popup_context self._activity_icon = None icon = IconButton(icon_name='theme:stock-zoom-mesh') @@ -106,7 +107,7 @@ class ZoomBox(hippo.CanvasBox): self.remove(self._activity_icon) if home_activity: - icon = ActivityButton(self._shell, home_activity) + icon = ActivityButton(self._shell, home_activity, self._popup_context) self.append(icon) self._activity_icon = icon else: diff --git a/shell/view/frame/clipboardbox.py b/shell/view/frame/clipboardbox.py index d230e062..d5e435ea 100644 --- a/shell/view/frame/clipboardbox.py +++ b/shell/view/frame/clipboardbox.py @@ -37,9 +37,9 @@ class _ContextMap: class ClipboardBox(hippo.CanvasBox): - def __init__(self, menu_shell): + def __init__(self, popup_context): hippo.CanvasBox.__init__(self) - self._menu_shell = menu_shell + self._popup_context = popup_context self._icons = {} self._context_map = _ContextMap() @@ -74,7 +74,7 @@ class ClipboardBox(hippo.CanvasBox): on_disk = False) def _object_added_cb(self, cb_service, object_id, name): - icon = ClipboardIcon(self._menu_shell, object_id, name) + icon = ClipboardIcon(self._popup_context, object_id, name) self.append(icon) self._icons[object_id] = icon diff --git a/shell/view/frame/clipboardpanelwindow.py b/shell/view/frame/clipboardpanelwindow.py index 5df7b037..30b9cb2e 100644 --- a/shell/view/frame/clipboardpanelwindow.py +++ b/shell/view/frame/clipboardpanelwindow.py @@ -17,10 +17,9 @@ class ClipboardPanelWindow(PanelWindow): clipboard = gtk.Clipboard() clipboard.connect("owner-change", self._owner_change_cb) - menu_shell = self.get_menu_shell() root = self.get_root() - box = ClipboardBox(menu_shell) + box = ClipboardBox(frame.get_popup_context()) root.append(box) # Receiving dnd drops diff --git a/shell/view/frame/frame.py b/shell/view/frame/frame.py index 8a3cb9d0..f8a89ee7 100644 --- a/shell/view/frame/frame.py +++ b/shell/view/frame/frame.py @@ -27,9 +27,9 @@ from view.frame.FriendsBox import FriendsBox from view.frame.PanelWindow import PanelWindow from view.frame.clipboardpanelwindow import ClipboardPanelWindow from view.frame.notificationtray import NotificationTray +from view.frame.framepopupcontext import FramePopupContext from model.ShellModel import ShellModel from sugar.graphics.timeline import Timeline -from sugar.graphics.menushell import MenuShell from sugar.graphics import units _ANIMATION = False @@ -62,6 +62,12 @@ class Frame: self._event_frame.connect('leave', self._event_frame_leave_cb) self._event_frame.show() + self._popup_context = FramePopupContext() + self._popup_context.connect('activated', + self._popup_context_activated_cb) + self._popup_context.connect('deactivated', + self._popup_context_deactivated_cb) + self._top_panel = self._create_top_panel() self._bottom_panel = self._create_bottom_panel() self._left_panel = self._create_left_panel() @@ -70,20 +76,11 @@ class Frame: shell.get_model().connect('notify::state', self._shell_state_changed_cb) - popup_context = shell.get_popup_context() - popup_context.connect('activated', - self._popup_context_activated_cb) - popup_context.connect('deactivated', - self._popup_context_deactivated_cb) - def _create_top_panel(self): panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL) - menu_shell = panel.get_menu_shell() root = panel.get_root() - menu_shell.set_position(MenuShell.BOTTOM) - - box = ZoomBox(self._shell, menu_shell) + box = ZoomBox(self._shell, self._popup_context) root.append(box) tray = NotificationTray() @@ -103,11 +100,8 @@ class Frame: def _create_bottom_panel(self): panel = self._create_panel(hippo.ORIENTATION_HORIZONTAL) - menu_shell = panel.get_menu_shell() root = panel.get_root() - menu_shell.set_position(MenuShell.TOP) - box = ActivitiesBox(self._shell) root.append(box) @@ -115,12 +109,9 @@ class Frame: def _create_right_panel(self): panel = self._create_panel(hippo.ORIENTATION_VERTICAL) - menu_shell = panel.get_menu_shell() root = panel.get_root() - menu_shell.set_position(MenuShell.LEFT) - - box = FriendsBox(self._shell, menu_shell) + box = FriendsBox(self._shell, self._popup_context) root.append(box) return panel @@ -158,19 +149,6 @@ class Frame: panel.connect('enter-notify-event', self._enter_notify_cb) panel.connect('leave-notify-event', self._leave_notify_cb) - menu_shell = panel.get_menu_shell() - menu_shell.connect('activated', - self._menu_shell_activated_cb) - menu_shell.connect('deactivated', - self._menu_shell_deactivated_cb) - - def _menu_shell_activated_cb(self, menu_shell): - self._timeline.goto('slide_in', True) - - def _menu_shell_deactivated_cb(self, menu_shell): - if self._mode != Frame.STICKY and not self._hover_frame: - self._timeline.play('before_slide_out', 'slide_out') - def _popup_context_activated_cb(self, popup_context): self._timeline.goto('slide_in', True) @@ -205,8 +183,7 @@ class Frame: def _leave_notify(self, panel): self._hover_frame = False - if not panel.get_menu_shell().is_active() and \ - not self._shell.get_popup_context().is_active() and \ + if not self._popup_context.is_active() and \ (self._mode == Frame.HIDE_ON_LEAVE or \ self._mode == Frame.AUTOMATIC): self._timeline.play('before_slide_out', 'slide_out') @@ -281,3 +258,6 @@ class Frame: def is_visible(self): return self._top_panel.props.visible + + def get_popup_context(self): + return self._popup_context diff --git a/shell/view/frame/framepopupcontext.py b/shell/view/frame/framepopupcontext.py new file mode 100644 index 00000000..cf35293a --- /dev/null +++ b/shell/view/frame/framepopupcontext.py @@ -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) diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index e32eabe9..9dfc6f37 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -98,6 +98,9 @@ class Menu(Popup): item.connect('button-press-event', self._item_button_press_event_cb) self.append(item) + def remove_item(self, item): + self.remove(item) + def add_separator(self): box = hippo.CanvasBox() box.props.background_color = color.MENU_SEPARATOR.get_int() From c9af045e43c9e26622b6a2db93304507064b2af7 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 21:15:08 +0100 Subject: [PATCH 14/29] Removed MenuIcon. --- shell/view/frame/ZoomBox.py | 1 - sugar/graphics/Makefile.am | 1 - sugar/graphics/menuicon.py | 80 ------------------------------------- 3 files changed, 82 deletions(-) delete mode 100644 sugar/graphics/menuicon.py diff --git a/shell/view/frame/ZoomBox.py b/shell/view/frame/ZoomBox.py index 57bc78a5..e77e350c 100644 --- a/shell/view/frame/ZoomBox.py +++ b/shell/view/frame/ZoomBox.py @@ -19,7 +19,6 @@ from gettext import gettext as _ import hippo -from sugar.graphics.menuicon import MenuIcon from sugar.graphics.menu import Menu, MenuItem from sugar.graphics.iconcolor import IconColor from sugar.graphics.iconbutton import IconButton diff --git a/sugar/graphics/Makefile.am b/sugar/graphics/Makefile.am index 7032066d..ee1c578d 100644 --- a/sugar/graphics/Makefile.am +++ b/sugar/graphics/Makefile.am @@ -12,7 +12,6 @@ sugar_PYTHON = \ iconcolor.py \ label.py \ menu.py \ - menuicon.py \ menushell.py \ optionmenu.py \ roundbox.py \ diff --git a/sugar/graphics/menuicon.py b/sugar/graphics/menuicon.py deleted file mode 100644 index 62d12754..00000000 --- a/sugar/graphics/menuicon.py +++ /dev/null @@ -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') From d4639a1764b0a846b3821366f2c4fb4fa409021d Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Wed, 21 Feb 2007 21:29:08 +0100 Subject: [PATCH 15/29] Use hippo.CanvasContext.translate_to_screen() for positioning popups. --- sugar/graphics/canvasicon.py | 3 +-- sugar/graphics/optionmenu.py | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/sugar/graphics/canvasicon.py b/sugar/graphics/canvasicon.py index 020e966d..fb4974a9 100644 --- a/sugar/graphics/canvasicon.py +++ b/sugar/graphics/canvasicon.py @@ -296,8 +296,7 @@ class CanvasIcon(hippo.CanvasBox, hippo.CanvasItem): if [x, y] == [None, None]: context = self.get_context() - #[x, y] = context.translate_to_screen(self) - [x, y] = context.translate_to_widget(self) + [x, y] = context.translate_to_screen(self) # TODO: Any better place to do this? popup.props.box_width = max(popup.props.box_width, diff --git a/sugar/graphics/optionmenu.py b/sugar/graphics/optionmenu.py index 2e6e4561..f2e4603c 100644 --- a/sugar/graphics/optionmenu.py +++ b/sugar/graphics/optionmenu.py @@ -150,8 +150,7 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem): self._menu.hide() else: context = self._round_box.get_context() - #[x, y] = context.translate_to_screen(self._round_box) - [x, y] = context.translate_to_widget(self._round_box) + [x, y] = context.translate_to_screen(self._round_box) # TODO: Any better place to do this? self._menu.props.box_width = self.get_width_request() From 5cf2b49cc1dfb1ff4c53d28bf6574fdb5da3b32e Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 00:07:08 +0100 Subject: [PATCH 16/29] Add an handle argument to the factory and to the activity constructor. --- shell/view/Shell.py | 14 ++++------ sugar/activity/Activity.py | 2 +- sugar/activity/Makefile.am | 1 + sugar/activity/activityfactory.py | 24 +++++++++++------ sugar/activity/activityfactoryservice.py | 6 +++-- sugar/activity/activityhandle.py | 34 ++++++++++++++++++++++++ 6 files changed, 61 insertions(+), 20 deletions(-) create mode 100644 sugar/activity/activityhandle.py diff --git a/shell/view/Shell.py b/shell/view/Shell.py index 232652a2..d2527d8f 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -20,7 +20,7 @@ import gobject import wnck from view.home.HomeWindow import HomeWindow -from sugar.presence import PresenceService +from sugar.activity.activityhandle import ActivityHandle from sugar.graphics.popupcontext import PopupContext from view.ActivityHost import ActivityHost from sugar.activity import activityfactory @@ -60,8 +60,6 @@ class Shell(gobject.GObject): self._frame = Frame(self) self._frame.show_and_hide(3) - self._pservice = PresenceService.get_instance() - self.start_activity('org.laptop.JournalActivity') def _activity_added_cb(self, home_model, home_activity): @@ -117,11 +115,6 @@ class Shell(gobject.GObject): activity.present() return - activity_ps = self._pservice.get_activity(activity_id) - if not activity_ps: - logging.error("Couldn't find shared activity for %s" % activity_id) - return - # Get the service name for this activity, if # we have a bundle on the system capable of handling # this activity type @@ -135,7 +128,10 @@ class Shell(gobject.GObject): home_model = self._model.get_home() home_model.notify_activity_launch(activity_id, act_type) - handler = activityfactory.create(act_type) + handle = ActivityHandle(activity_id) + handle.pservice_id = activity_id + + handler = activityfactory.create(act_type, handle) handler.connect('success', self._join_success_cb, activity_ps) handler.connect('error', self._join_error_cb, home_model) diff --git a/sugar/activity/Activity.py b/sugar/activity/Activity.py index e34b73e9..befcc31f 100644 --- a/sugar/activity/Activity.py +++ b/sugar/activity/Activity.py @@ -26,7 +26,7 @@ from sugar import env class Activity(gtk.Window): """Base Activity class that all other Activities derive from.""" - def __init__(self): + def __init__(self, activity_handle): gtk.Window.__init__(self) self.connect('destroy', self._destroy_cb) diff --git a/sugar/activity/Makefile.am b/sugar/activity/Makefile.am index df6af5cb..87c42651 100644 --- a/sugar/activity/Makefile.am +++ b/sugar/activity/Makefile.am @@ -4,6 +4,7 @@ sugar_PYTHON = \ Activity.py \ activityfactory.py \ activityfactoryservice.py \ + activityhandle.py \ activityservice.py \ bundle.py \ bundlebuilder.py \ diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index e2a0d4d2..68781030 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -23,6 +23,7 @@ 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" @@ -40,12 +41,17 @@ class ActivityCreationHandler(gobject.GObject): ([gobject.TYPE_PYOBJECT])) } - def __init__(self, service_name): + def __init__(self, service_name, activity_handle): gobject.GObject.__init__(self) - self._activity_id = self._find_unique_activity_id() - if not self._activity_id: - raise RuntimeError("Cannot generate activity id.") + if activity_handle: + self._activity_handle = activity_handle + else: + activity_id = self._find_unique_activity_id() + if activity_id: + self._activity_handle = ActivityHandle(activity_id) + else: + raise RuntimeError("Cannot generate activity id.") registry = bundleregistry.get_registry() bundle = registry.get_bundle(service_name) @@ -54,10 +60,12 @@ class ActivityCreationHandler(gobject.GObject): proxy_obj = bus.get_object(service_name, bundle.get_object_path()) factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory") - factory.create(reply_handler=self._reply_handler, error_handler=self._error_handler) + factory.create(str(self._activity_handle), + reply_handler=self._reply_handler, + error_handler=self._error_handler) def get_activity_id(self): - return self._activity_id + return self._activity_handle.activity_id def _find_unique_activity_id(self): pservice = PresenceService.get_instance() @@ -93,6 +101,6 @@ class ActivityCreationHandler(gobject.GObject): logging.debug("Couldn't create activity: %s" % err) self.emit('error', err) -def create(service_name): +def create(service_name, activity_handle=None): """Create a new activity from its name.""" - return ActivityCreationHandler(service_name) + return ActivityCreationHandler(service_name, activity_handle) diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py index 15ebf144..e5200104 100644 --- a/sugar/activity/activityfactoryservice.py +++ b/sugar/activity/activityfactoryservice.py @@ -21,6 +21,7 @@ import dbus import dbus.service from sugar.activity.bundle import Bundle +from sugar.activity import activityhandle from sugar import logger class ActivityFactoryService(dbus.service.Object): @@ -48,8 +49,9 @@ class ActivityFactoryService(dbus.service.Object): dbus.service.Object.__init__(self, bus_name, object_path) @dbus.service.method("com.redhat.Sugar.ActivityFactory") - def create(self): - activity = self._constructor() + def create(self, handle): + activity_handle = activityhandle.create_from_string(handle) + activity = self._constructor(activity_handle) self._activities.append(activity) activity.connect('destroy', self._activity_destroy_cb) diff --git a/sugar/activity/activityhandle.py b/sugar/activity/activityhandle.py new file mode 100644 index 00000000..f9d1f301 --- /dev/null +++ b/sugar/activity/activityhandle.py @@ -0,0 +1,34 @@ +# 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 + + def __str__(self): + return self.activity_id + + def get_presence_service(): + pservice = PresenceService.get_instance() + return pservice.get_activity(self._pservice_id) + +def create_from_string(handle): + activity_handle = ActivityHandle(handle) + activity_handle.pservice_id = handle From 0556d5f51f05e7effde34cfa7d0e977a5b204ef1 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 00:14:00 +0100 Subject: [PATCH 17/29] Fix caps --- sugar/activity/Makefile.am | 2 +- sugar/activity/{Activity.py => activity.py} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename sugar/activity/{Activity.py => activity.py} (100%) diff --git a/sugar/activity/Makefile.am b/sugar/activity/Makefile.am index 87c42651..d11a3474 100644 --- a/sugar/activity/Makefile.am +++ b/sugar/activity/Makefile.am @@ -1,7 +1,7 @@ sugardir = $(pythondir)/sugar/activity sugar_PYTHON = \ __init__.py \ - Activity.py \ + activity.py \ activityfactory.py \ activityfactoryservice.py \ activityhandle.py \ diff --git a/sugar/activity/Activity.py b/sugar/activity/activity.py similarity index 100% rename from sugar/activity/Activity.py rename to sugar/activity/activity.py From 0d7bdeb20a6a807852a92aa5e854d1f5bfa9d82f Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 00:41:26 +0100 Subject: [PATCH 18/29] Improve handle marshalling. Add an uri to the handle. --- sugar/activity/activityfactory.py | 2 +- sugar/activity/activityfactoryservice.py | 4 +-- sugar/activity/activityhandle.py | 31 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index 68781030..61484662 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -60,7 +60,7 @@ class ActivityCreationHandler(gobject.GObject): proxy_obj = bus.get_object(service_name, bundle.get_object_path()) factory = dbus.Interface(proxy_obj, "com.redhat.Sugar.ActivityFactory") - factory.create(str(self._activity_handle), + factory.create(self._activity_handle.get_dict(), reply_handler=self._reply_handler, error_handler=self._error_handler) diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py index e5200104..82fae4ac 100644 --- a/sugar/activity/activityfactoryservice.py +++ b/sugar/activity/activityfactoryservice.py @@ -48,9 +48,9 @@ class ActivityFactoryService(dbus.service.Object): object_path = '/' + service_name.replace('.', '/') dbus.service.Object.__init__(self, bus_name, object_path) - @dbus.service.method("com.redhat.Sugar.ActivityFactory") + @dbus.service.method("com.redhat.Sugar.ActivityFactory", in_signature="a{ss}") def create(self, handle): - activity_handle = activityhandle.create_from_string(handle) + activity_handle = activityhandle.create_from_dict(handle) activity = self._constructor(activity_handle) self._activities.append(activity) diff --git a/sugar/activity/activityhandle.py b/sugar/activity/activityhandle.py index f9d1f301..24c9d673 100644 --- a/sugar/activity/activityhandle.py +++ b/sugar/activity/activityhandle.py @@ -21,14 +21,29 @@ class ActivityHandle(object): def __init__(self, activity_id): self.activity_id = activity_id self.pservice_id = None + self.uri = None - def __str__(self): - return self.activity_id + 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_presence_service(): - pservice = PresenceService.get_instance() - return pservice.get_activity(self._pservice_id) + 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 -def create_from_string(handle): - activity_handle = ActivityHandle(handle) - activity_handle.pservice_id = handle + 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 From 0b6b6cd6acfedd3bfc326623ad0ccff21c5c4d5e Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 00:57:49 +0100 Subject: [PATCH 19/29] Cleanup the Activity API, code needs more love. --- shell/view/Shell.py | 10 +--------- sugar/activity/activity.py | 30 ++++++++---------------------- sugar/activity/activityfactory.py | 15 ++++++--------- sugar/activity/activityservice.py | 17 ----------------- 4 files changed, 15 insertions(+), 57 deletions(-) diff --git a/shell/view/Shell.py b/shell/view/Shell.py index d2527d8f..812698fb 100644 --- a/shell/view/Shell.py +++ b/shell/view/Shell.py @@ -103,9 +103,6 @@ class Shell(gobject.GObject): def get_popup_context(self): return self._popup_context - def _join_success_cb(self, handler, activity, activity_ps): - activity.join(activity_ps.object_path()) - def _join_error_cb(self, handler, err, home_model): home_mode.notify_activity_launch_failed(handler.get_activity_id()) @@ -132,13 +129,9 @@ class Shell(gobject.GObject): handle.pservice_id = activity_id handler = activityfactory.create(act_type, handle) - handler.connect('success', self._join_success_cb, activity_ps) handler.connect('error', self._join_error_cb, home_model) - def _start_success_cb(self, handler, activity): - activity.start(handler.get_activity_id()) - - def _start_error_cb(self, handler, err, home_model, activity_id, activity_type): + def _start_error_cb(self, handler, err, home_model): home_model.notify_activity_launch_failed(handler.get_activity_id()) def start_activity(self, activity_type): @@ -150,7 +143,6 @@ class Shell(gobject.GObject): home_model.notify_activity_launch(handler.get_activity_id(), activity_type) - handler.connect('success', self._start_success_cb) handler.connect('error', self._start_error_cb, home_model) # Zoom to Home for launch feedback diff --git a/sugar/activity/activity.py b/sugar/activity/activity.py index befcc31f..3b7d9cec 100644 --- a/sugar/activity/activity.py +++ b/sugar/activity/activity.py @@ -26,16 +26,19 @@ from sugar import env class Activity(gtk.Window): """Base Activity class that all other Activities derive from.""" - def __init__(self, activity_handle): + def __init__(self, handle): gtk.Window.__init__(self) self.connect('destroy', self._destroy_cb) self._shared = False - self._activity_id = None - self._service = None + 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() @@ -44,14 +47,6 @@ class Activity(gtk.Window): self._bus = ActivityService(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 - self.present() def get_type(self): @@ -70,13 +65,8 @@ class Activity(gtk.Window): """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() - + def _join(self, service): + self._service = service self._shared = True # Publish the default service, it's a copy of @@ -103,10 +93,6 @@ class Activity(gtk.Window): 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 diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index 61484662..90b49f71 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -36,14 +36,13 @@ class ActivityCreationHandler(gobject.GObject): '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, service_name, activity_handle): gobject.GObject.__init__(self) + self._service_name = service_name + if activity_handle: self._activity_handle = activity_handle else: @@ -91,14 +90,12 @@ class ActivityCreationHandler(gobject.GObject): return act_id def _reply_handler(self, xid): - bus = dbus.SessionBus() - proxy_obj = bus.get_object(_ACTIVITY_SERVICE_NAME + '%d' % xid, - _ACTIVITY_SERVICE_PATH + "/%s" % xid) - activity = dbus.Interface(proxy_obj, _ACTIVITY_INTERFACE) - self.emit('success', activity) + 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" % 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): diff --git a/sugar/activity/activityservice.py b/sugar/activity/activityservice.py index 5c1654aa..c5a99e08 100644 --- a/sugar/activity/activityservice.py +++ b/sugar/activity/activityservice.py @@ -40,18 +40,6 @@ class ActivityService(dbus.service.Object): dbus.service.Object.__init__(self, bus_name, object_path) 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): @@ -72,8 +60,3 @@ class ActivityService(dbus.service.Object): 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) From 1b5ac988d786199a2ec2732775e8acd67833458d Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 01:23:58 +0100 Subject: [PATCH 20/29] Put exec back, I forgot it was used by the camera. Helper method to create an activity with an uri. --- sugar/activity/activity.py | 4 ++ sugar/activity/activityfactory.py | 63 +++++++++++++++---------------- sugar/activity/activityservice.py | 6 +++ 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/sugar/activity/activity.py b/sugar/activity/activity.py index 3b7d9cec..c706b7a9 100644 --- a/sugar/activity/activity.py +++ b/sugar/activity/activity.py @@ -93,6 +93,10 @@ class Activity(gtk.Window): 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 diff --git a/sugar/activity/activityfactory.py b/sugar/activity/activityfactory.py index 90b49f71..bcf70b2b 100644 --- a/sugar/activity/activityfactory.py +++ b/sugar/activity/activityfactory.py @@ -30,6 +30,28 @@ _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__ = { @@ -42,15 +64,7 @@ class ActivityCreationHandler(gobject.GObject): gobject.GObject.__init__(self) self._service_name = service_name - - if activity_handle: - self._activity_handle = activity_handle - else: - activity_id = self._find_unique_activity_id() - if activity_id: - self._activity_handle = ActivityHandle(activity_id) - else: - raise RuntimeError("Cannot generate activity id.") + self._activity_handle = activity_handle registry = bundleregistry.get_registry() bundle = registry.get_bundle(service_name) @@ -66,29 +80,6 @@ class ActivityCreationHandler(gobject.GObject): def get_activity_id(self): return self._activity_handle.activity_id - def _find_unique_activity_id(self): - 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: - act_id = None - continue - - return act_id - def _reply_handler(self, xid): logging.debug("Activity created %s (%s)." % (self._activity_handle.activity_id, self._service_name)) @@ -100,4 +91,12 @@ class ActivityCreationHandler(gobject.GObject): 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) diff --git a/sugar/activity/activityservice.py b/sugar/activity/activityservice.py index c5a99e08..defeb086 100644 --- a/sugar/activity/activityservice.py +++ b/sugar/activity/activityservice.py @@ -60,3 +60,9 @@ class ActivityService(dbus.service.Object): 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) + From 1d020027198b94015d559f99943f49aebd98a690 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 22 Feb 2007 12:07:20 +0100 Subject: [PATCH 21/29] Add default for X servers that return DPI 0 and round the result as does Mozilla. --- lib/src/sugar-utils.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/src/sugar-utils.c b/lib/src/sugar-utils.c index 7aa896eb..f7bf1c5a 100644 --- a/lib/src/sugar-utils.c +++ b/lib/src/sugar-utils.c @@ -17,9 +17,11 @@ * Boston, MA 02111-1307, USA. */ -#include +#include #include +#include "sugar-utils.h" + gint sugar_get_screen_dpi(void) { @@ -27,8 +29,8 @@ sugar_get_screen_dpi(void) if (val) { char *e; double d = strtod(val, &e); - if (e != val) - return round(d); + if (d > 0.0) + return (int)(d+0.5); } return 96; From 2f4822f1cdcd4b87d29523d54e70bddf4d20c2b9 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 22 Feb 2007 12:34:38 +0100 Subject: [PATCH 22/29] Make OptionMenu use the new Menu and MenuItem classes. --- sugar/graphics/menu.py | 2 +- sugar/graphics/optionmenu.py | 93 +++++++++++------------------------- tests/test-option-menu.py | 9 ++-- 3 files changed, 34 insertions(+), 70 deletions(-) diff --git a/sugar/graphics/menu.py b/sugar/graphics/menu.py index 9dfc6f37..0f154701 100644 --- a/sugar/graphics/menu.py +++ b/sugar/graphics/menu.py @@ -80,7 +80,7 @@ class Menu(Popup): 'action': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object])) } - def __init__(self, title): + def __init__(self, title=None): Popup.__init__(self) self.props.background_color = color.MENU_BACKGROUND.get_int() diff --git a/sugar/graphics/optionmenu.py b/sugar/graphics/optionmenu.py index f2e4603c..edd1d39e 100644 --- a/sugar/graphics/optionmenu.py +++ b/sugar/graphics/optionmenu.py @@ -24,70 +24,27 @@ import hippo from sugar.graphics import units from sugar.graphics.roundbox import RoundBox +from sugar.graphics.menu import Menu, MenuItem from sugar.graphics import iconbutton from sugar.graphics import color from sugar.graphics import font from sugar.graphics.canvasicon import CanvasIcon -class Menu(hippo.CanvasBox, hippo.CanvasItem): - __gtype_name__ = 'SugarMenu' - - __gsignals__ = { - 'action': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([object])) - } - +class _Menu(Menu): def __init__(self): - hippo.CanvasBox.__init__(self) - self.props.background_color = color.MENU_BACKGROUND.get_int() - self.props.border_color = color.MENU_BORDER.get_int() - self.props.border = units.points_to_pixels(1) - self._window = None - - def add_item(self, action_id, label, icon_name=None, icon_color=None): - box = hippo.CanvasBox(orientation=hippo.ORIENTATION_HORIZONTAL) - box.props.padding = 5 - box.props.spacing = 5 - if icon_name: - icon = CanvasIcon(icon_name=icon_name, - scale=units.SMALL_ICON_SCALE) - if icon_color: - icon.props.color = icon_color - box.append(icon) - - canvas_text = hippo.CanvasText() - canvas_text.props.text = label - canvas_text.props.color = color.LABEL_TEXT.get_int() - canvas_text.props.font_desc = font.DEFAULT.get_pango_desc() - box.append(canvas_text) - - box.connect('button-press-event', self._item_button_press_event_cb, - [action_id, label]) - self.append(box) - - def add_separator(self): - box = hippo.CanvasBox() - box.props.background_color = color.MENU_SEPARATOR.get_int() - box.props.box_height = units.points_to_pixels(1) - self.append(box) - - def show(self, x, y): - if not self._window: - self._window = hippo.CanvasWindow(gtk.WINDOW_POPUP) - self._window.move(x, y) - self._window.set_root(self) - self._window.show() - - def hide(self): - if self._window: - self._window.destroy() - self._window = None - - def _item_button_press_event_cb(self, item, event, data): - self.emit('action', data) - self.hide() + Menu.__init__(self) + self._is_visible = False def is_visible(self): - return self._window != None + return self._is_visible + + def popup(self, x, y): + Menu.popup(self, x, y) + self._is_visible = True + + def popdown(self): + Menu.popdown(self) + self._is_visible = False class OptionMenu(hippo.CanvasBox, hippo.CanvasItem): __gtype_name__ = 'SugarOptionMenu' @@ -122,8 +79,9 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem): arrow.props.xalign = hippo.ALIGNMENT_START self._round_box.append(arrow) - self._menu = Menu() + self._menu = _Menu() self._menu.connect('action', self._menu_action_cb) + self._menu.connect('action-completed', self._menu_action_completed_cb) self.connect('button-press-event', self._button_press_event_cb) @@ -135,19 +93,19 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem): if pspec.name == 'value': return self._value - def add_option(self, action_id, label, icon_name=None, icon_color=None): + def add_item(self, menu_item): if not self._value: - self._value = action_id - self._canvas_text.props.text = label + self._value = menu_item.props.action_id + self._canvas_text.props.text = menu_item.props.label - self._menu.add_item(action_id, label, icon_name, icon_color) + self._menu.add_item(menu_item) def add_separator(self): self._menu.add_separator() def _button_press_event_cb(self, box, event): if self._menu.is_visible(): - self._menu.hide() + self._menu.popdown() else: context = self._round_box.get_context() [x, y] = context.translate_to_screen(self._round_box) @@ -156,11 +114,16 @@ class OptionMenu(hippo.CanvasBox, hippo.CanvasItem): self._menu.props.box_width = self.get_width_request() [width, height] = self._round_box.get_allocation() - self._menu.show(x, y + height) + self._menu.popup(x, y + height) - def _menu_action_cb(self, menu, data): - [action_id, label] = data + def _menu_action_cb(self, menu, menu_item): + action_id = menu_item.props.action_id + label = menu_item.props.label + if action_id != self._value: self._value = action_id self._canvas_text.props.text = label self.emit('changed') + + def _menu_action_completed_cb(self, menu): + self._menu.popdown() diff --git a/tests/test-option-menu.py b/tests/test-option-menu.py index a3c6e6dc..bbcc03a0 100755 --- a/tests/test-option-menu.py +++ b/tests/test-option-menu.py @@ -24,6 +24,7 @@ import hippo from sugar.graphics.toolbar import Toolbar from sugar.graphics.optionmenu import OptionMenu +from sugar.graphics.menu import MenuItem from sugar.graphics.iconbutton import IconButton def _option_menu_changed_cb(option_menu): @@ -56,11 +57,11 @@ OPTION_WRITE = 3 OPTION_CHAT = 4 option_menu = OptionMenu() -option_menu.add_option(OPTION_ANYTHING, _('Anything')) +option_menu.add_item(MenuItem(OPTION_ANYTHING, _('Anything'))) option_menu.add_separator() -option_menu.add_option(OPTION_DRAW, _('Draw'), 'theme:stock-close') -option_menu.add_option(OPTION_WRITE, _('Write')) -option_menu.add_option(OPTION_CHAT, _('Chat')) +option_menu.add_item(MenuItem(OPTION_DRAW, _('Draw'), 'theme:stock-close')) +option_menu.add_item(MenuItem(OPTION_WRITE, _('Write'))) +option_menu.add_item(MenuItem(OPTION_CHAT, _('Chat'))) option_menu.connect('changed', _option_menu_changed_cb) toolbar.append(option_menu) From 228908b36e53f966ca97760973f4ea395d3e2d20 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 22 Feb 2007 13:21:09 +0100 Subject: [PATCH 23/29] Use the new icons for object types. --- services/clipboard/typeregistry.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/services/clipboard/typeregistry.py b/services/clipboard/typeregistry.py index 6fca3854..3f42ad5a 100644 --- a/services/clipboard/typeregistry.py +++ b/services/clipboard/typeregistry.py @@ -29,7 +29,7 @@ class TextFileType(FileType): return _('Text snippet') def get_icon(self): - return 'theme:activity-xbook' + return 'theme:object-text' def get_preview(self): for format, data in self._formats.iteritems(): @@ -57,7 +57,7 @@ class ImageFileType(FileType): return _('Image') def get_icon(self): - return 'theme:activity-sketch' + return 'theme:object-image' def get_preview(self): return '' @@ -77,7 +77,7 @@ class UriFileType(FileType): return _('Web Page') def get_icon(self): - return 'theme:activity-web' + return 'theme:object-link' def get_preview(self): for format, data in self._formats.iteritems(): @@ -103,7 +103,7 @@ class PdfFileType(FileType): return _('PDF file') def get_icon(self): - return 'theme:activity-xbook' + return 'theme:object-text' def get_preview(self): return '' @@ -123,7 +123,7 @@ class MsWordFileType(FileType): return _('MS Word file') def get_icon(self): - return 'theme:activity-abiword' + return 'theme:object-text' def get_preview(self): return '' @@ -143,7 +143,7 @@ class RtfFileType(FileType): return _('RTF file') def get_icon(self): - return 'theme:activity-abiword' + return 'theme:object-text' def get_preview(self): return '' @@ -163,7 +163,7 @@ class OOTextFileType(FileType): return _('OpenOffice text file') def get_icon(self): - return 'theme:activity-abiword' + return 'theme:object-text' def get_preview(self): return '' From 995c59cb3e461af2c74d9a084705e5f7972bcbdb Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 14:11:50 +0100 Subject: [PATCH 24/29] Make sugar-activity-factory just a wrapper. --- shell/sugar-activity-factory | 17 +---------------- sugar/activity/activityfactoryservice.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 22 deletions(-) diff --git a/shell/sugar-activity-factory b/shell/sugar-activity-factory index 588d2d5b..009b4609 100755 --- a/shell/sugar-activity-factory +++ b/shell/sugar-activity-factory @@ -18,21 +18,6 @@ import sys -import pygtk -pygtk.require('2.0') -import gobject -import gtk - -import dbus.glib - -# Work around for dbus mutex locking issue -gobject.threads_init() -dbus.glib.threads_init() - from sugar.activity import activityfactoryservice -sys.path.insert(0, sys.argv[2]) - -activityfactoryservice.start(sys.argv[1], sys.argv[2]) - -gtk.main() +activityfactoryservice.run(sys.argv) diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py index 82fae4ac..303d1994 100644 --- a/sugar/activity/activityfactoryservice.py +++ b/sugar/activity/activityfactoryservice.py @@ -16,16 +16,24 @@ # Boston, MA 02111-1307, USA. import os +import sys +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): - """Dbus service that takes care of creating new instances of an activity""" + """D-Bus service that creates new instances of an activity""" def __init__(self, service_name, activity_class): self._activities = [] @@ -67,15 +75,17 @@ class ActivityFactoryService(dbus.service.Object): if len(self._activities) == 0: gtk.main_quit() -def start(activity_class, bundle_path): +def run(args): """Start the activity factory.""" - bundle = Bundle(bundle_path) + sys.path.insert(0, args[2]) + + bundle = Bundle(args[2]) logger.start(bundle.get_name()) - os.environ['SUGAR_BUNDLE_PATH'] = bundle_path + os.environ['SUGAR_BUNDLE_PATH'] = args[2] 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(), - activity_class) + factory = ActivityFactoryService(bundle.get_service_name(), args[1]) + gtk.main() From 02f375b7108037b6a1dc2f002f03091aa4853b8a Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 15:46:13 +0100 Subject: [PATCH 25/29] Add a class attribute as per the updated spec. Cleanups. --- sugar/activity/activityfactoryservice.py | 14 ++++++++++---- sugar/activity/bundle.py | 20 +++++++++++++++++--- sugar/activity/bundleregistry.py | 9 ++------- 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/sugar/activity/activityfactoryservice.py b/sugar/activity/activityfactoryservice.py index 303d1994..822bf99d 100644 --- a/sugar/activity/activityfactoryservice.py +++ b/sugar/activity/activityfactoryservice.py @@ -17,6 +17,7 @@ import os import sys +from optparse import OptionParser import gobject import gtk @@ -77,15 +78,20 @@ class ActivityFactoryService(dbus.service.Object): def run(args): """Start the activity factory.""" - sys.path.insert(0, args[2]) + parser = OptionParser() + parser.add_option("-p", "--bundle-path", dest="bundle_path", + help="path to the activity bundle") + (options, args) = parser.parse_args() - bundle = Bundle(args[2]) + sys.path.insert(0, options.bundle_path) + + bundle = Bundle(options.bundle_path) logger.start(bundle.get_name()) - os.environ['SUGAR_BUNDLE_PATH'] = args[2] + 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[1]) + factory = ActivityFactoryService(bundle.get_service_name(), args[0]) gtk.main() diff --git a/sugar/activity/bundle.py b/sugar/activity/bundle.py index 8c17958a..6a6ebdd7 100644 --- a/sugar/activity/bundle.py +++ b/sugar/activity/bundle.py @@ -1,8 +1,11 @@ import logging import os - from ConfigParser import ConfigParser +from sugar import env + +_PYTHON_FACTORY='sugar-activity-factory' + class Bundle: """Info about an activity bundle. Wraps the activity.info file.""" def __init__(self, path): @@ -38,11 +41,18 @@ class Bundle: self._valid = False logging.error('%s must specify a name' % self._path) - if cp.has_option(section, 'exec'): + if cp.has_option(section, 'class'): + self._class = cp.get(section, 'class') + self._exec = '%s %s --bundle-path=%s' % ( + os.path.join(env.get_shell_bin_dir(), _PYTHON_FACTORY), + self._class, self.get_path()) + elif cp.has_option(section, 'exec'): + self._class = None self._exec = cp.get(section, 'exec') else: + self._exec = None self._valid = False - logging.error('%s must specify an exec' % self._path) + logging.error('%s must specify exec or class' % self._path) if cp.has_option(section, 'show_launcher'): if cp.get(section, 'show_launcher') == 'no': @@ -94,6 +104,10 @@ class Bundle: """Get the command to execute to launch the activity factory""" return self._exec + def get_class(self): + """Get the main Activity class""" + return self._exec + def get_show_launcher(self): """Get whether there should be a visible launcher for the activity""" return self._show_launcher diff --git a/sugar/activity/bundleregistry.py b/sugar/activity/bundleregistry.py index ccf3b794..08f543ff 100644 --- a/sugar/activity/bundleregistry.py +++ b/sugar/activity/bundleregistry.py @@ -11,13 +11,8 @@ class _ServiceManager(object): self._path = env.get_user_service_dir() def add(self, bundle): - name = bundle.get_service_name() - - # FIXME evil hack. Probably need to fix Exec spec - full_exec = env.get_shell_bin_dir() + '/' + bundle.get_exec() - full_exec += ' ' + bundle.get_path() - - util.write_service(name, full_exec, self._path) + util.write_service(bundle.get_service_name(), + bundle.get_exec(), self._path) class BundleRegistry(gobject.GObject): """Service that tracks the available activity bundles""" From c4435397fc2367743acf980b3c978614b59bb28f Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 15:55:07 +0100 Subject: [PATCH 26/29] Remove bundle specific stuff from env. Move get_bundle_path to activity. --- sugar/activity/activity.py | 12 +++++++----- sugar/activity/activityservice.py | 6 +++--- sugar/env.py | 18 ------------------ 3 files changed, 10 insertions(+), 26 deletions(-) diff --git a/sugar/activity/activity.py b/sugar/activity/activity.py index c706b7a9..3c6f64a6 100644 --- a/sugar/activity/activity.py +++ b/sugar/activity/activity.py @@ -21,7 +21,6 @@ import gtk from sugar.presence import PresenceService from sugar.activity.activityservice import ActivityService -from sugar import env class Activity(gtk.Window): """Base Activity class that all other Activities derive from.""" @@ -49,13 +48,13 @@ class Activity(gtk.Window): self.present() - def get_type(self): - """Gets the activity type.""" - return env.get_bundle_service_name() + 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 env.get_bundle_default_type() + return os.environ['SUGAR_BUNDLE_DEFAULT_TYPE'] def get_shared(self): """Returns TRUE if the activity is shared on the mesh.""" @@ -103,3 +102,6 @@ class Activity(gtk.Window): self._bus = None if self._service: self._pservice.unregister_service(self._service) + +def get_bundle_path(): + return os.environ['SUGAR_BUNDLE_BUNDLE_PATH'] diff --git a/sugar/activity/activityservice.py b/sugar/activity/activityservice.py index defeb086..9a7efb3b 100644 --- a/sugar/activity/activityservice.py +++ b/sugar/activity/activityservice.py @@ -52,9 +52,9 @@ class ActivityService(dbus.service.Object): return self._activity.get_id() @dbus.service.method(_ACTIVITY_INTERFACE) - def get_type(self): - """Get the activity type""" - return self._activity.get_type() + 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): diff --git a/sugar/env.py b/sugar/env.py index c0df585b..afaa5653 100644 --- a/sugar/env.py +++ b/sugar/env.py @@ -24,30 +24,12 @@ try: except ImportError: from sugar.__installed__ import * -def get_bundle_path(): - if os.environ.has_key('SUGAR_BUNDLE_PATH'): - return os.environ['SUGAR_BUNDLE_PATH'] - else: - return None - def is_emulator(): if os.environ.has_key('SUGAR_EMULATOR'): if os.environ['SUGAR_EMULATOR'] == 'yes': return True return False -def get_bundle_service_name(): - if os.environ.has_key('SUGAR_BUNDLE_SERVICE_NAME'): - return os.environ['SUGAR_BUNDLE_SERVICE_NAME'] - else: - return None - -def get_bundle_default_type(): - if os.environ.has_key('SUGAR_BUNDLE_DEFAULT_TYPE'): - return os.environ['SUGAR_BUNDLE_DEFAULT_TYPE'] - else: - return None - def get_profile_path(): if os.environ.has_key('SUGAR_PROFILE'): profile_id = os.environ['SUGAR_PROFILE'] From fc805d3a8890156cd74aeca91f1be7f200053481 Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 17:27:00 +0100 Subject: [PATCH 27/29] Adapt to activity api change --- shell/model/homeactivity.py | 2 +- sugar/activity/activity.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/shell/model/homeactivity.py b/shell/model/homeactivity.py index 4b5a1e37..d4962f31 100644 --- a/shell/model/homeactivity.py +++ b/shell/model/homeactivity.py @@ -82,7 +82,7 @@ class HomeActivity(gobject.GObject): act_id = self._service.get_id() if act_id != self._id: raise RuntimeError("Activity's real ID (%s) didn't match expected (%s)." % (act_id, self._id)) - act_type = self._service.get_type() + act_type = self._service.get_service_name() if act_type != self._type: raise RuntimeError("Activity's real type (%s) didn't match expected (%s)." % (act_type, self._type)) diff --git a/sugar/activity/activity.py b/sugar/activity/activity.py index 3c6f64a6..3e039882 100644 --- a/sugar/activity/activity.py +++ b/sugar/activity/activity.py @@ -16,6 +16,7 @@ # Boston, MA 02111-1307, USA. import logging +import os import gtk From 8679737d86471f2b7f3a6d44f7ee131cd3e83967 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Thu, 22 Feb 2007 17:39:06 +0100 Subject: [PATCH 28/29] Rename for new API. --- shell/model/homemodel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py index ff04dfcd..7d3205b4 100644 --- a/shell/model/homemodel.py +++ b/shell/model/homemodel.py @@ -130,7 +130,7 @@ class HomeModel(gobject.GObject): else: # activity got lost, took longer to launch than we allow, # or it was launched by something other than the shell - act_type = act_service.get_type() + act_type = act_service.get_service_name() bundle = self._bundle_registry.get_bundle(act_type) if not bundle: raise RuntimeError("No bundle for activity type '%s'." % act_type) From 2c896e35b1c3517bf235d0b1cd954f814226c5dd Mon Sep 17 00:00:00 2001 From: Marco Pesenti Gritti Date: Thu, 22 Feb 2007 17:55:46 +0100 Subject: [PATCH 29/29] Pass the right parameters to HomeActivity --- shell/model/homemodel.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/model/homemodel.py b/shell/model/homemodel.py index 7d3205b4..9d7d68cb 100644 --- a/shell/model/homemodel.py +++ b/shell/model/homemodel.py @@ -135,7 +135,7 @@ class HomeModel(gobject.GObject): if not bundle: raise RuntimeError("No bundle for activity type '%s'." % act_type) return - activity = HomeActivity(act_service, bundle) + activity = HomeActivity(bundle, act_id) self._activities[act_id] = activity activity.set_window(window)