From b4f9eddc10b7196b364cfd8fc99140d2ad3841de Mon Sep 17 00:00:00 2001
From: Tomeu Vizoso <[EMAIL PROTECTED]>
Date: Wed, 2 Apr 2008 14:59:40 +0200
Subject: [PATCH] Add notifications for activity launch and invitations.

---
 src/view/devices/battery.py          |    1 +
 src/view/devices/network/mesh.py     |    1 +
 src/view/devices/network/wireless.py |    1 +
 src/view/frame/Makefile.am           |    1 +
 src/view/frame/activitiestray.py     |  154 +++++++++++++++++++++++++++++++++-
 src/view/frame/clipboardtray.py      |    2 +-
 src/view/frame/frame.py              |   30 +++++++
 src/view/frame/frameinvoker.py       |    1 -
 src/view/home/activitiesring.py      |    4 +-
 src/view/palettes.py                 |   10 +-
 src/view/pulsingicon.py              |   17 ++++
 11 files changed, 209 insertions(+), 13 deletions(-)

diff --git a/src/view/devices/battery.py b/src/view/devices/battery.py
index 01f8100..8a4caf0 100644
--- a/src/view/devices/battery.py
+++ b/src/view/devices/battery.py
@@ -38,6 +38,7 @@ class DeviceView(TrayIcon):
 
         self._model = model
         self.palette = BatteryPalette(_('My Battery life'))
+        self.set_palette(self.palette)
         self.palette.props.invoker = FrameWidgetInvoker(self)
         self.palette.set_group_id('frame')
 
diff --git a/src/view/devices/network/mesh.py b/src/view/devices/network/mesh.py
index 121e56a..bcdc0b2 100644
--- a/src/view/devices/network/mesh.py
+++ b/src/view/devices/network/mesh.py
@@ -36,6 +36,7 @@ class DeviceView(TrayIcon):
         self._model = model
 
         self.palette = MeshPalette(_("Mesh Network"), model)
+        self.set_palette(self.palette)
         self.palette.props.invoker = FrameWidgetInvoker(self)
         self.palette.set_group_id('frame')
 
diff --git a/src/view/devices/network/wireless.py b/src/view/devices/network/wireless.py
index ff1ba53..6b50769 100644
--- a/src/view/devices/network/wireless.py
+++ b/src/view/devices/network/wireless.py
@@ -46,6 +46,7 @@ class DeviceView(TrayIcon):
 
         self._counter = 0
         self.palette = WirelessPalette(self._get_palette_primary_text(), meshdev)
+        self.set_palette(self.palette)
         self.palette.props.invoker = FrameWidgetInvoker(self)
         self.palette.set_group_id('frame')
         self.palette.set_frequency(self._model.props.frequency)
diff --git a/src/view/frame/Makefile.am b/src/view/frame/Makefile.am
index b02085a..3c5dc79 100644
--- a/src/view/frame/Makefile.am
+++ b/src/view/frame/Makefile.am
@@ -9,6 +9,7 @@ sugar_PYTHON =				\
 	friendstray.py			\
 	eventarea.py			\
 	frame.py			\
+	notification.py			\
 	overlaybox.py			\
 	framewindow.py			\
 	zoomtoolbar.py
diff --git a/src/view/frame/activitiestray.py b/src/view/frame/activitiestray.py
index e2a448e..4fbd5f6 100644
--- a/src/view/frame/activitiestray.py
+++ b/src/view/frame/activitiestray.py
@@ -1,3 +1,4 @@
+# Copyright (C) 2006-2007 Red Hat, Inc.
 # Copyright (C) 2008 One Laptop Per Child
 #
 # This program is free software; you can redistribute it and/or modify
@@ -23,11 +24,18 @@ from sugar.graphics import style
 from sugar.graphics.tray import HTray
 from sugar.graphics.xocolor import XoColor
 from sugar.graphics.radiotoolbutton import RadioToolButton
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.icon import Icon
+from sugar.graphics.palette import Palette, WidgetInvoker
+from sugar.graphics.menuitem import MenuItem
+from sugar import activity
 
 from model import shellmodel
 from view.palettes import JournalPalette, CurrentActivityPalette
 from view.pulsingicon import PulsingIcon
 from view.frame.frameinvoker import FrameWidgetInvoker
+from view.frame.notification import NotificationIcon
+import view.frame.frame
 
 class ActivityButton(RadioToolButton):
     def __init__(self, home_activity, group):
@@ -48,9 +56,9 @@ class ActivityButton(RadioToolButton):
         self._icon.show()
 
         if self._home_activity.get_type() == "org.laptop.JournalActivity":
-            palette = JournalPalette(self, self._home_activity)
+            palette = JournalPalette(self._home_activity)
         else:
-            palette = CurrentActivityPalette(self, self._home_activity)
+            palette = CurrentActivityPalette(self._home_activity)
         palette.props.invoker = FrameWidgetInvoker(self)
         palette.set_group_id('frame')
         self.set_palette(palette)
@@ -59,23 +67,136 @@ class ActivityButton(RadioToolButton):
             self._icon.props.pulsing = True
             self._notify_launching_hid = home_activity.connect('notify::launching',
                     self.__notify_launching_cb)
+
+            self._notif_icon = NotificationIcon()
+            self._notif_icon.props.xo_color = home_activity.get_icon_color()
+            if home_activity.get_icon_path():
+                self._notif_icon.props.icon_filename = home_activity.get_icon_path()
+            else:
+                self._notif_icon.props.icon_name = 'image-missing'
+            view.frame.frame.get_instance().add_notification(self._notif_icon)
         else:
             self._notify_launching_hid = None
+            self._notif_icon = None
 
     def __notify_launching_cb(self, home_activity, pspec):
-        self._icon.props.pulsing = False
-        home_activity.disconnect(self._notify_launching_hid)
+        if not home_activity.props.launching:
+            if self._notif_icon is not None:
+                frame = view.frame.frame.get_instance()
+                frame.remove_notification(self._notif_icon)
+                self._notif_icon = None
+            self._icon.props.pulsing = False
+            home_activity.disconnect(self._notify_launching_hid)
+
+class InviteButton(ToolButton):
+    def __init__(self, activity_model):
+        ToolButton.__init__(self)
+
+        self._activity_model = activity_model
+
+        self._icon = Icon()
+        self._icon.props.xo_color = activity_model.get_color()
+        if activity_model.get_icon_name():
+            self._icon.props.file = activity_model.get_icon_name()
+        else:
+            self._icon.props.icon_name = 'image-missing'
+        self.set_icon_widget(self._icon)
+        self._icon.show()
+
+        palette = InvitePalette(activity_model)
+        palette.props.invoker = FrameWidgetInvoker(self)
+        palette.set_group_id('frame')
+        self.set_palette(palette)
+
+        self.connect('clicked', self.__clicked_cb)
+        self.connect('destroy', self.__destroy_cb)
+
+        self._notif_icon = NotificationIcon()
+        self._notif_icon.props.xo_color = activity_model.get_color()
+        if activity_model.get_icon_name():
+            self._notif_icon.props.icon_filename = activity_model.get_icon_name()
+        else:
+            self._notif_icon.props.icon_name = 'image-missing'
+        self._notif_icon.connect('button-release-event',
+                                 self.__button_release_event_cb)
+
+        palette = InvitePalette(activity_model)
+        palette.props.invoker = WidgetInvoker(self._notif_icon)
+        palette.set_group_id('frame')
+        self._notif_icon.palette = palette
+
+        view.frame.frame.get_instance().add_notification(self._notif_icon)
+
+    def __button_release_event_cb(self, icon, event):
+        self.emit('clicked')
+
+    def __clicked_cb(self, button):
+        if self._notif_icon is not None:
+            frame = view.frame.frame.get_instance()
+            frame.remove_notification(self._notif_icon)
+            self._notif_icon = None
+
+        shell = view.Shell.get_instance()
+        shell.join_activity(self._activity_model.get_bundle_id(),
+                            self._activity_model.get_id())
+
+    def __destroy_cb(self, button):
+            frame = view.frame.frame.get_instance()
+            frame.remove_notification(self._notif_icon)
+    
+class InvitePalette(Palette):
+    def __init__(self, activity_model):
+        self._activity_model = activity_model
+
+        Palette.__init__(self, '')
+
+        registry = activity.get_registry()
+        activity_info = registry.get_activity(activity_model.get_bundle_id())
+        if activity_info:
+            self.set_primary_text(activity_info.name)
+        else:
+            self.set_primary_text(activity_model.get_bundle_id())
+
+        menu_item = MenuItem(_('Join'), icon_name='dialog-ok')
+        menu_item.connect('activate', self.__join_activate_cb)
+        self.menu.append(menu_item)
+        menu_item.show()
+
+        menu_item = MenuItem(_('Decline'), icon_name='dialog-cancel')
+        menu_item.connect('activate', self.__decline_activate_cb)
+        self.menu.append(menu_item)
+        menu_item.show()
+
+    def __join_activate_cb(self, menu_item):
+        shell = view.Shell.get_instance()
+        shell.join_activity(self._activity_model.get_bundle_id(),
+                            self._activity_model.get_id())
+
+    def __decline_activate_cb(self, menu_item):
+        invites = shellmodel.get_instance().get_invites()
+        for invite in invites:
+            if invite.get_activity_id() == self._activity_model.get_id():
+                invites.remove_invite(invite)
+                return
 
 class ActivitiesTray(HTray):
     def __init__(self):
         HTray.__init__(self)
 
         self._buttons = {}
+        self._invite_to_item = {}
+
         self._home_model = shellmodel.get_instance().get_home()
         self._home_model.connect('activity-added', self.__activity_added_cb)
         self._home_model.connect('activity-removed', self.__activity_removed_cb)
         self._home_model.connect('pending-activity-changed', self.__activity_changed_cb)
 
+        self._invites = shellmodel.get_instance().get_invites()
+        for invite in self._invites:
+            self._add_invite(invite)
+        self._invites.connect('invite-added', self.__invite_added_cb)
+        self._invites.connect('invite-removed', self.__invite_removed_cb)
+
     def __activity_added_cb(self, home_model, home_activity):
         logging.debug('__activity_added_cb: %r' % home_activity)
         if self.get_children():
@@ -103,3 +224,28 @@ class ActivitiesTray(HTray):
     def __activity_toggled_cb(self, button, home_activity):
         home_activity.get_window().activate(1)
 
+    def __invite_clicked_cb(self, icon, invite):
+        self._invites.remove_invite(invite)
+    
+    def __invite_added_cb(self, invites, invite):
+        self._add_invite(invite)
+
+    def __invite_removed_cb(self, invites, invite):
+        self._remove_invite(invite)
+
+    def _add_invite(self, invite):
+        mesh = shellmodel.get_instance().get_mesh()
+        activity_model = mesh.get_activity(invite.get_activity_id())
+        if activity_model:
+            item = InviteButton(activity_model)
+            item.connect('clicked', self.__invite_clicked_cb, invite)
+            self.add_item(item)
+            item.show()
+
+            self._invite_to_item[invite] = item
+
+    def _remove_invite(self, invite):
+        self.remove_item(self._invite_to_item[invite])
+        self._invite_to_item[invite].destroy()
+        del self._invite_to_item[invite]
+
diff --git a/src/view/frame/clipboardtray.py b/src/view/frame/clipboardtray.py
index e4e7b5f..044aafd 100644
--- a/src/view/frame/clipboardtray.py
+++ b/src/view/frame/clipboardtray.py
@@ -108,7 +108,7 @@ class ClipboardTray(VTray):
         icon.show()
         self._icons[object_id] = icon
 
-        objects_to_delete = self.get_children()[self.MAX_ITEMS:]
+        objects_to_delete = self.get_children()[:-self.MAX_ITEMS]
         for icon in objects_to_delete:
             logging.debug('ClipboardTray: deleting surplus object')
             cb_service = clipboardservice.get_instance()
diff --git a/src/view/frame/frame.py b/src/view/frame/frame.py
index 0cafeff..2871420 100644
--- a/src/view/frame/frame.py
+++ b/src/view/frame/frame.py
@@ -33,9 +33,11 @@ from view.frame.friendstray import FriendsTray
 from view.frame.devicestray import DevicesTray
 from view.frame.framewindow import FrameWindow
 from view.frame.clipboardpanelwindow import ClipboardPanelWindow
+from view.frame.notification import NotificationIcon, NotificationWindow
 from model.shellmodel import ShellModel
 
 _FRAME_HIDING_DELAY = 500
+_NOTIFICATION_DURATION = 5000
 
 class _Animation(animator.Animation):
     def __init__(self, frame, end):
@@ -121,6 +123,8 @@ class Frame(object):
         self._key_listener = _KeyListener(self)
         self._mouse_listener = _MouseListener(self)
 
+        self._notif_by_icon = {}
+
     def is_visible(self):
         return self.current_position != 0.0
 
@@ -279,6 +283,32 @@ class Frame(object):
     def notify_key_press(self):
         self._key_listener.key_press()
 
+    def add_notification(self, icon):
+        if not isinstance(icon, NotificationIcon):
+            raise TypeError('icon must be a NotificationIcon.')
+        
+        window = NotificationWindow()
+        window.add(icon)
+        icon.show()
+        window.show()
+        
+        self._notif_by_icon[icon] = window
+
+        gobject.timeout_add(_NOTIFICATION_DURATION,
+                        lambda: self.remove_notification(icon))
+
+    def remove_notification(self, icon):
+        if not isinstance(icon, NotificationIcon):
+            raise TypeError('icon must be a NotificationIcon.')
+
+        if icon not in self._notif_by_icon:
+            logging.debug('icon %r not in list of notifications.' % icon)
+            return
+
+        window = self._notif_by_icon[icon]
+        window.destroy()
+        del self._notif_by_icon[icon]
+
     visible = property(is_visible, None)
 
 _instance = None
diff --git a/src/view/frame/frameinvoker.py b/src/view/frame/frameinvoker.py
index 607290e..31f5933 100644
--- a/src/view/frame/frameinvoker.py
+++ b/src/view/frame/frameinvoker.py
@@ -18,7 +18,6 @@ import gtk
 
 from sugar.graphics import style
 from sugar.graphics.palette import Palette
-from sugar.graphics.palette import CanvasInvoker
 from sugar.graphics.palette import WidgetInvoker
 
 def _get_screen_area():
diff --git a/src/view/home/activitiesring.py b/src/view/home/activitiesring.py
index 9d84e30..52d3427 100644
--- a/src/view/home/activitiesring.py
+++ b/src/view/home/activitiesring.py
@@ -202,9 +202,9 @@ class CurrentActivityIcon(CanvasIcon, hippo.CanvasItem):
         self.props.size = style.STANDARD_ICON_SIZE
 
         if home_activity.get_type() == "org.laptop.JournalActivity":
-            palette = JournalPalette(self, home_activity)
+            palette = JournalPalette(home_activity)
         else:
-            palette = CurrentActivityPalette(self, home_activity)
+            palette = CurrentActivityPalette(home_activity)
         self.set_palette(palette)
 
     def __pending_activity_changed_cb(self, home_model, home_activity):
diff --git a/src/view/palettes.py b/src/view/palettes.py
index ff8fad6..1180e95 100644
--- a/src/view/palettes.py
+++ b/src/view/palettes.py
@@ -28,7 +28,7 @@ from sugar.graphics.menuitem import MenuItem
 from sugar.graphics.icon import Icon
 
 class BasePalette(Palette):
-    def __init__(self, widget, home_activity):
+    def __init__(self, home_activity):
         Palette.__init__(self, '', menu_after_content=True)
 
         if home_activity.props.launching:
@@ -45,9 +45,9 @@ class BasePalette(Palette):
         raise NotImplementedError
 
 class CurrentActivityPalette(BasePalette):
-    def __init__(self, widget, home_activity):
+    def __init__(self, home_activity):
         self._home_activity = home_activity
-        BasePalette.__init__(self, widget, home_activity)
+        BasePalette.__init__(self, home_activity)
 
     def setup_palette(self):
         self.set_primary_text(self._home_activity.get_title())
@@ -89,12 +89,12 @@ class CurrentActivityPalette(BasePalette):
         self._home_activity.get_window().close(1)
 
 class JournalPalette(BasePalette):
-    def __init__(self, widget, home_activity):
+    def __init__(self, home_activity):
         self._home_activity = home_activity
         self._progress_bar = None
         self._free_space_label = None
 
-        BasePalette.__init__(self, widget, home_activity)
+        BasePalette.__init__(self, home_activity)
 
     def setup_palette(self):
         self.set_primary_text(self._home_activity.get_title())
diff --git a/src/view/pulsingicon.py b/src/view/pulsingicon.py
index 6d8e53f..0a4a825 100644
--- a/src/view/pulsingicon.py
+++ b/src/view/pulsingicon.py
@@ -75,6 +75,13 @@ class PulsingIcon(Icon):
 
         Icon.__init__(self, **kwargs)
 
+        self._palette = None
+        self.connect('destroy', self.__destroy_cb)
+
+    def __destroy_cb(self, icon):
+        if self._palette is not None:
+            self._palette.destroy()
+
     # Hack for sharing code between CanvasPulsingIcon and PulsingIcon
     _get_as_rgba = _get_as_rgba
     _update_colors = _update_colors
@@ -136,6 +143,16 @@ class PulsingIcon(Icon):
         else:
             return Icon.do_get_property(self, pspec)
 
+    def _get_palette(self):
+        return self._palette
+    
+    def _set_palette(self, palette):
+        if self._palette is not None:        
+            self._palette.props.invoker = None
+        self._palette = palette
+    
+    palette = property(_get_palette, _set_palette)
+
 class CanvasPulsingIcon(CanvasIcon):
     __gtype_name__ = 'SugarCanvasPulsingIcon'
 
-- 
1.5.2.5

_______________________________________________
Sugar mailing list
[email protected]
http://lists.laptop.org/listinfo/sugar

Reply via email to