Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Abente <martin.abente.lahaye@gmail.com>2010-12-21 15:00:49 (GMT)
committer Bernie Innocenti <bernie@codewiz.org>2011-01-08 21:44:33 (GMT)
commit84d7fc8cac35313cb3d70374b6e2d8d6e2187203 (patch)
tree07d239dfb186fd21128d50ebe7e88f57fc59fc70
parent28244016560b53dd8d7227867415f09484c673cc (diff)
Sugar: Notification system integration
Signed-off-by: Anish Mangal <anish@sugarlabs.org>
-rw-r--r--rpms/sugar/0001-Simple-messages-notification-extension.patch578
-rw-r--r--rpms/sugar/0002-Improve-message-notification-behaviour.patch131
-rw-r--r--rpms/sugar/0003-Yum-updater-notifications-integration.patch78
-rw-r--r--rpms/sugar/sugar.spec11
4 files changed, 797 insertions, 1 deletions
diff --git a/rpms/sugar/0001-Simple-messages-notification-extension.patch b/rpms/sugar/0001-Simple-messages-notification-extension.patch
new file mode 100644
index 0000000..b33be4f
--- /dev/null
+++ b/rpms/sugar/0001-Simple-messages-notification-extension.patch
@@ -0,0 +1,578 @@
+From 53511484c6e43877de14a02234aa7a0e3e6bc0e7 Mon Sep 17 00:00:00 2001
+From: Martin Abente <martin.abente.lahaye@gmail.com>
+Date: Wed, 1 Dec 2010 10:27:44 -0300
+Subject: [PATCH] Simple messages notification extension
+
+Extend jarabe.frame.notification with new graphical
+elements in order to display message notifications.
+These graphical elements were inspired from Gary
+Martin mockups.
+
+Messages notification are accessible through dbus
+see http://library.gnome.org/devel/notification-spec/
+or jarabe.frame.frame.add_message method.
+This implementation only supports icons, summary
+and markup body.
+
+When a message is received:
+
+1. A notification icon will appear and remain
+ as long as the time defined by the caller.
+
+2. A new tray button will be added to the respective
+ tray, this button will remain present until the
+ user reads its content to delete it explicitly.
+
+3. The button constains a message palette that will
+ behave as a messages queue.
+
+Icons-only notications will be accesible and will behave
+as before.
+
+VERSION 2: The messages queue was moved from the corners
+ to the respective trays, in order to mantain
+ the corners available for other usage.
+---
+ src/jarabe/frame/frame.py | 126 ++++++++++++++++++++---
+ src/jarabe/frame/notification.py | 202 +++++++++++++++++++++++++++++++++++---
+ src/jarabe/view/pulsingicon.py | 24 +++++
+ 3 files changed, 319 insertions(+), 33 deletions(-)
+
+diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py
+index 55f866f..83bff06 100644
+--- a/src/jarabe/frame/frame.py
++++ b/src/jarabe/frame/frame.py
+@@ -19,6 +19,7 @@ import logging
+ import gtk
+ import gobject
+ import hippo
++import os
+
+ from sugar.graphics import animator
+ from sugar.graphics import style
+@@ -33,6 +34,7 @@ from jarabe.frame.devicestray import DevicesTray
+ from jarabe.frame.framewindow import FrameWindow
+ from jarabe.frame.clipboardpanelwindow import ClipboardPanelWindow
+ from jarabe.frame.notification import NotificationIcon, NotificationWindow
++from jarabe.frame.notification import NotificationButton, HistoryPalette
+ from jarabe.model import notifications
+
+ TOP_RIGHT = 0
+@@ -43,6 +45,8 @@ BOTTOM_LEFT = 3
+ _FRAME_HIDING_DELAY = 500
+ _NOTIFICATION_DURATION = 5000
+
++_DEFAULT_ICON = 'emblem-notification'
++
+ class _Animation(animator.Animation):
+ def __init__(self, frame, end):
+ start = frame.current_position
+@@ -114,6 +118,10 @@ class Frame(object):
+ self._event_area.connect('enter', self._enter_corner_cb)
+ self._event_area.show()
+
++ self._activities_tray = None
++ self._devices_tray = None
++ self._friends_tray = None
++
+ self._top_panel = self._create_top_panel()
+ self._bottom_panel = self._create_bottom_panel()
+ self._left_panel = self._create_left_panel()
+@@ -126,6 +134,7 @@ class Frame(object):
+ self._mouse_listener = _MouseListener(self)
+
+ self._notif_by_icon = {}
++ self._notif_by_message = {}
+
+ notification_service = notifications.get_service()
+ notification_service.notification_received.connect(
+@@ -185,6 +194,7 @@ class Frame(object):
+ panel.append(hippo.CanvasWidget(widget=activities_tray),
+ hippo.PACK_EXPAND)
+ activities_tray.show()
++ self._activities_tray = activities_tray
+
+ return panel
+
+@@ -195,6 +205,7 @@ class Frame(object):
+ devices_tray = DevicesTray()
+ panel.append(hippo.CanvasWidget(widget=devices_tray), hippo.PACK_EXPAND)
+ devices_tray.show()
++ self._devices_tray = devices_tray
+
+ return panel
+
+@@ -204,6 +215,7 @@ class Frame(object):
+ tray = FriendsTray()
+ panel.append(hippo.CanvasWidget(widget=tray), hippo.PACK_EXPAND)
+ tray.show()
++ self._friends_tray = tray
+
+ return panel
+
+@@ -279,15 +291,7 @@ class Frame(object):
+ def _enter_corner_cb(self, event_area):
+ self._mouse_listener.mouse_enter()
+
+- def notify_key_press(self):
+- self._key_listener.key_press()
+-
+- def add_notification(self, icon, corner=gtk.CORNER_TOP_LEFT,
+- duration=_NOTIFICATION_DURATION):
+-
+- if not isinstance(icon, NotificationIcon):
+- raise TypeError('icon must be a NotificationIcon.')
+-
++ def _create_notification_window(self, corner):
+ window = NotificationWindow()
+
+ screen = gtk.gdk.screen_get_default()
+@@ -303,6 +307,46 @@ class Frame(object):
+ else:
+ raise ValueError('Inalid corner: %r' % corner)
+
++ return window
++
++ def _add_message_button(self, button, corner):
++ if corner == gtk.CORNER_BOTTOM_RIGHT:
++ self._devices_tray.add_item(button)
++ elif corner == gtk.CORNER_TOP_RIGHT:
++ self._friends_tray.add_item(button)
++ else:
++ self._activities_tray.add_item(button)
++
++ def _remove_message_button(self, button, corner):
++ if corner == gtk.CORNER_BOTTOM_RIGHT:
++ self._devices_tray.remove_item(button)
++ elif corner == gtk.CORNER_TOP_RIGHT:
++ self._friends_tray.remove_item(button)
++ else:
++ self._activities_tray.remove_item(button)
++
++ def _launch_notification_icon(self, icon_name, xo_color,
++ position, duration):
++ icon = NotificationIcon()
++ icon.props.xo_color = xo_color
++
++ if icon_name.startswith(os.sep):
++ icon.props.icon_filename = icon_name
++ else:
++ icon.props.icon_name = icon_name
++
++ self.add_notification(icon, position, duration)
++
++ def notify_key_press(self):
++ self._key_listener.key_press()
++
++ def add_notification(self, icon, corner=gtk.CORNER_TOP_LEFT,
++ duration=_NOTIFICATION_DURATION):
++
++ if not isinstance(icon, NotificationIcon):
++ raise TypeError('icon must be a NotificationIcon.')
++
++ window = self._create_notification_window(corner)
+ window.add(icon)
+ icon.show()
+ window.show()
+@@ -321,28 +365,76 @@ class Frame(object):
+ window.destroy()
+ del self._notif_by_icon[icon]
+
++ def add_message(self, body, summary='', icon_name=_DEFAULT_ICON,
++ xo_color=None, corner=gtk.CORNER_TOP_LEFT,
++ duration=_NOTIFICATION_DURATION):
++
++ if xo_color is None:
++ xo_color = profile.get_color()
++
++ button = self._notif_by_message.get(corner, None)
++ if button is None:
++ button = NotificationButton(xo_color)
++ button.show()
++ self._add_message_button(button, corner)
++ self._notif_by_message[corner] = button
++
++ button.start_pulsing()
++
++ palette = button.get_palette()
++ if palette is None:
++ palette = HistoryPalette()
++ palette.set_group_id('frame')
++ palette.connect('clear-messages', self.remove_message, corner)
++ button.set_palette(palette)
++
++ palette.push_message(body, summary, icon_name, xo_color)
++ self._launch_notification_icon(icon_name, xo_color, corner, duration)
++
++
++ def remove_message(self, palette, corner):
++ if corner not in self._notif_by_message:
++ logging.debug('Button %s is not active', str(corner))
++ return
++
++ button = self._notif_by_message[corner]
++ self._remove_message_button(button, corner)
++ del self._notif_by_message[corner]
++
+ def __notification_received_cb(self, **kwargs):
+ logging.debug('__notification_received_cb %r', kwargs)
+- icon = NotificationIcon()
+
+ hints = kwargs['hints']
+
+- icon_file_name = hints.get('x-sugar-icon-file-name', '')
+- if icon_file_name:
+- icon.props.icon_filename = icon_file_name
+- else:
+- icon.props.icon_name = 'application-octet-stream'
++ icon_name = hints.get('x-sugar-icon-file-name', '')
++ if not icon_name:
++ icon_name = _DEFAULT_ICON
+
+ icon_colors = hints.get('x-sugar-icon-colors', '')
+ if not icon_colors:
+ icon_colors = profile.get_color()
+- icon.props.xo_color = icon_colors
+
+ duration = kwargs.get('expire_timeout', -1)
+ if duration == -1:
+ duration = _NOTIFICATION_DURATION
+
+- self.add_notification(icon, gtk.CORNER_TOP_RIGHT, duration)
++ category = hints.get('category', '')
++ if category == 'device':
++ position = gtk.CORNER_BOTTOM_RIGHT
++ elif category == 'presence':
++ position = gtk.CORNER_TOP_RIGHT
++ else:
++ position = gtk.CORNER_TOP_LEFT
++
++ summary = kwargs.get('summary', '')
++ body = kwargs.get('body', '')
++
++ if summary or body:
++ self.add_message(body, summary, icon_name,
++ icon_colors, position, duration)
++ else:
++ self._launch_notification_icon(icon_name, icon_colors,
++ position, duration)
+
+ def __notification_cancelled_cb(self, **kwargs):
+ # Do nothing for now. Our notification UI is so simple, there's no
+diff --git a/src/jarabe/frame/notification.py b/src/jarabe/frame/notification.py
+index 83dc27e..34b7c1e 100644
+--- a/src/jarabe/frame/notification.py
++++ b/src/jarabe/frame/notification.py
+@@ -1,4 +1,6 @@
+ # Copyright (C) 2008 One Laptop Per Child
++# Copyright (C) 2010 Martin Abente
++# Copyright (C) 2010 Aleksey Lim
+ #
+ # This program is free software; you can redistribute it and/or modify
+ # it under the terms of the GNU General Public License as published by
+@@ -16,12 +18,182 @@
+
+ import gobject
+ import gtk
++import re
++import os
++
++from gettext import gettext as _
+
+ from sugar.graphics import style
+ from sugar.graphics.xocolor import XoColor
++from sugar.graphics.palette import Palette
++from sugar.graphics.menuitem import MenuItem
++from sugar.graphics.toolbutton import ToolButton
++from sugar import profile
+
++from jarabe.frame.frameinvoker import FrameWidgetInvoker
+ from jarabe.view.pulsingicon import PulsingIcon
+
++
++_PULSE_TIMEOUT = 3
++_PULSE_COLOR = XoColor('%s,%s' % \
++ (style.COLOR_BUTTON_GREY.get_svg(), style.COLOR_TRANSPARENT.get_svg()))
++_BODY_FILTERS = "<img.*?/>"
++_NOTIFICATION_ICON = 'emblem-notification'
++
++
++def _create_pulsing_icon(icon_name, xo_color):
++ icon = PulsingIcon(
++ pixel_size=style.STANDARD_ICON_SIZE,
++ pulse_color=_PULSE_COLOR,
++ base_color=xo_color,
++ timeout=_PULSE_TIMEOUT,
++ )
++
++ if icon_name.startswith(os.sep):
++ icon.props.file = icon_name
++ else:
++ icon.props.icon_name = icon_name
++
++ return icon
++
++class _HistoryIconWidget(gtk.Alignment):
++ __gtype_name__ = 'SugarHistoryIconWidget'
++
++ def __init__(self, icon_name, xo_color):
++ icon = _create_pulsing_icon(icon_name, xo_color)
++ icon.props.pulsing = True
++
++ gtk.Alignment.__init__(self, xalign=0.5, yalign=0.0)
++ self.props.top_padding = style.DEFAULT_PADDING
++ self.set_size_request(
++ style.GRID_CELL_SIZE - style.FOCUS_LINE_WIDTH * 2,
++ style.GRID_CELL_SIZE - style.DEFAULT_PADDING)
++ self.add(icon)
++
++
++class _HistorySummaryWidget(gtk.Alignment):
++ __gtype_name__ = 'SugarHistorySummaryWidget'
++
++ def __init__(self, summary):
++ summary_label = gtk.Label()
++ summary_label.props.wrap = True
++ summary_label.set_markup(
++ '<b>%s</b>' % gobject.markup_escape_text(summary))
++
++ gtk.Alignment.__init__(self, xalign=0.0, yalign=1.0)
++ self.props.right_padding = style.DEFAULT_SPACING
++ self.add(summary_label)
++
++
++class _HistoryBodyWidget(gtk.Alignment):
++ __gtype_name__ = 'SugarHistoryBodyWidget'
++
++ def __init__(self, body):
++ body_label = gtk.Label()
++ body_label.props.wrap = True
++ body_label.set_markup(body)
++
++ gtk.Alignment.__init__(self, xalign=0, yalign=0.0)
++ self.props.right_padding = style.DEFAULT_SPACING
++ self.add(body_label)
++
++
++class _MessagesHistoryBox(gtk.VBox):
++ __gtype_name__ = 'SugarMessagesHistoryBox'
++
++ def __init__(self):
++ gtk.VBox.__init__(self)
++ self._setup_links_style()
++
++ def _setup_links_style(self):
++ # XXX: find a better way to change style for upstream
++ link_color = profile.get_color().get_fill_color()
++ visited_link_color = profile.get_color().get_stroke_color()
++
++ links_style='''
++ style "label" {
++ GtkLabel::link-color="%s"
++ GtkLabel::visited-link-color="%s"
++ }
++ widget_class "*GtkLabel" style "label"
++ ''' % (link_color, visited_link_color)
++ gtk.rc_parse_string(links_style)
++
++ def push_message(self, body, summary, icon_name, xo_color):
++ entry = gtk.HBox()
++
++ icon_widget = _HistoryIconWidget(icon_name, xo_color)
++ entry.pack_start(icon_widget, False)
++
++ message = gtk.VBox()
++ message.props.border_width = style.DEFAULT_PADDING
++ entry.pack_start(message)
++
++ if summary:
++ summary_widget = _HistorySummaryWidget(summary)
++ message.pack_start(summary_widget, False)
++
++ body = re.sub(_BODY_FILTERS, '', body)
++
++ if body:
++ body_widget = _HistoryBodyWidget(body)
++ message.pack_start(body_widget)
++
++ entry.show_all()
++ self.pack_start(entry)
++ self.reorder_child(entry, 0)
++
++ self_width_, self_height = self.size_request()
++ if (self_height > gtk.gdk.screen_height() / 4 * 3) and \
++ (len(self.get_children()) > 1):
++ self.remove(self.get_children()[-1])
++
++
++class HistoryPalette(Palette):
++ __gtype_name__ = 'SugarHistoryPalette'
++
++ __gsignals__ = {
++ 'clear-messages': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
++ }
++
++ def __init__(self):
++ Palette.__init__(self)
++
++ self.set_accept_focus(False)
++
++ self._messages_box = _MessagesHistoryBox()
++ self._messages_box.show()
++
++ palette_box = self.get_children()[0]
++ primary_box = palette_box.get_children()[0]
++ primary_box.hide()
++ palette_box.add(self._messages_box)
++ palette_box.reorder_child(self._messages_box, 0)
++
++ clear_option = MenuItem(_('Clear history'), 'dialog-cancel')
++ clear_option.connect('activate', self.__clear_messages_cb)
++ clear_option.show()
++
++ self.menu.append(clear_option)
++
++ def __clear_messages_cb(self, clear_option):
++ self.emit('clear-messages')
++
++ def push_message(self, body, summary, icon_name, xo_color):
++ self._messages_box.push_message(body, summary, icon_name, xo_color)
++
++class NotificationButton(ToolButton):
++
++ def __init__(self, xo_color):
++ ToolButton.__init__(self)
++ self._icon = _create_pulsing_icon(_NOTIFICATION_ICON, xo_color)
++ self.set_icon_widget(self._icon)
++ self._icon.show()
++ self.set_palette_invoker(FrameWidgetInvoker(self))
++
++ def start_pulsing(self):
++ self._icon.props.pulsing = True
++
+ class NotificationIcon(gtk.EventBox):
+ __gtype_name__ = 'SugarNotificationIcon'
+
+@@ -31,27 +203,29 @@ class NotificationIcon(gtk.EventBox):
+ 'icon-filename' : (str, None, None, None, gobject.PARAM_READWRITE)
+ }
+
+- _PULSE_TIMEOUT = 3
+-
+ def __init__(self, **kwargs):
+ self._icon = PulsingIcon(pixel_size=style.STANDARD_ICON_SIZE)
+ gobject.GObject.__init__(self, **kwargs)
+ self.props.visible_window = False
++ self.set_app_paintable(True)
+
+- self._icon.props.pulse_color = \
+- XoColor('%s,%s' % (style.COLOR_BUTTON_GREY.get_svg(),
+- style.COLOR_TRANSPARENT.get_svg()))
+- self._icon.props.pulsing = True
++ color = gtk.gdk.color_parse(style.COLOR_BLACK.get_html())
++ self.modify_bg(gtk.STATE_PRELIGHT, color)
++
++ color = gtk.gdk.color_parse(style.COLOR_BUTTON_GREY.get_html())
++ self.modify_bg(gtk.STATE_ACTIVE, color)
++
++ self._icon.props.pulse_color = _PULSE_COLOR
++ self._icon.props.timeout = _PULSE_TIMEOUT
+ self.add(self._icon)
+ self._icon.show()
+
+- gobject.timeout_add_seconds(self._PULSE_TIMEOUT, self.__stop_pulsing_cb)
++ self.start_pulsing()
+
+ self.set_size_request(style.GRID_CELL_SIZE, style.GRID_CELL_SIZE)
+
+- def __stop_pulsing_cb(self):
+- self._icon.props.pulsing = False
+- return False
++ def start_pulsing(self):
++ self._icon.props.pulsing = True
+
+ def do_set_property(self, pspec, value):
+ if pspec.name == 'xo-color':
+@@ -83,18 +257,14 @@ class NotificationIcon(gtk.EventBox):
+ class NotificationWindow(gtk.Window):
+ __gtype_name__ = 'SugarNotificationWindow'
+
+- def __init__(self, **kwargs):
+-
+- gtk.Window.__init__(self, **kwargs)
++ def __init__(self):
++ gtk.Window.__init__(self, gtk.WINDOW_POPUP)
+
+ self.set_decorated(False)
+ self.set_resizable(False)
+ self.connect('realize', self._realize_cb)
+
+ def _realize_cb(self, widget):
+- self.window.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG)
+- self.window.set_accept_focus(False)
+-
+ color = gtk.gdk.color_parse(style.COLOR_TOOLBAR_GREY.get_html())
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+diff --git a/src/jarabe/view/pulsingicon.py b/src/jarabe/view/pulsingicon.py
+index 43ec358..27fa53c 100644
+--- a/src/jarabe/view/pulsingicon.py
++++ b/src/jarabe/view/pulsingicon.py
+@@ -92,12 +92,23 @@ class PulsingIcon(Icon):
+ self._pulse_color = None
+ self._paused = False
+ self._pulsing = False
++ self._timeout = 0
++ self._pulsing_sid = None
+
+ Icon.__init__(self, **kwargs)
+
+ self._palette = None
+ self.connect('destroy', self.__destroy_cb)
+
++ def set_timeout(self, timeout):
++ self._timeout = timeout
++
++ def get_timeout(self):
++ return self._timeout
++
++ timeout = gobject.property(
++ type=int, getter=get_timeout, setter=set_timeout)
++
+ def set_pulse_color(self, pulse_color):
+ self._pulse_color = pulse_color
+ self._pulser.update()
+@@ -133,10 +144,20 @@ class PulsingIcon(Icon):
+ type=bool, default=False, getter=get_paused, setter=set_paused)
+
+ def set_pulsing(self, pulsing):
++ if self._pulsing == pulsing:
++ return
++
++ if self._pulsing_sid is not None:
++ gobject.source_remove(self._pulsing_sid)
++ self._pulsing_sid = None
++
+ self._pulsing = pulsing
+
+ if self._pulsing:
+ self._pulser.start(restart=True)
++ if self.props.timeout > 0:
++ self._pulsing_sid = gobject.timeout_add_seconds(
++ self.props.timeout, self.__timeout_cb)
+ else:
+ self._pulser.stop()
+
+@@ -156,6 +177,9 @@ class PulsingIcon(Icon):
+
+ palette = property(_get_palette, _set_palette)
+
++ def __timeout_cb(self):
++ self.props.pulsing = False
++
+ def __destroy_cb(self, icon):
+ self._pulser.stop()
+ if self._palette is not None:
+--
+1.7.1
+
diff --git a/rpms/sugar/0002-Improve-message-notification-behaviour.patch b/rpms/sugar/0002-Improve-message-notification-behaviour.patch
new file mode 100644
index 0000000..ddc118e
--- /dev/null
+++ b/rpms/sugar/0002-Improve-message-notification-behaviour.patch
@@ -0,0 +1,131 @@
+From bc297c80dac88b8629b3373509cf6af2e9656497 Mon Sep 17 00:00:00 2001
+From: Martin Abente <martin.abente.lahaye@gmail.com>
+Date: Mon, 20 Dec 2010 14:27:32 -0300
+Subject: [PATCH] Improve message notification behaviour
+
+Corner's notification icon will use the same icon
+as the frame notification button.
+
+Frame's notification button will pulse until
+the messages are read.
+---
+ src/jarabe/frame/frame.py | 9 +++++----
+ src/jarabe/frame/notification.py | 26 ++++++++++++++++++--------
+ 2 files changed, 23 insertions(+), 12 deletions(-)
+
+diff --git a/src/jarabe/frame/frame.py b/src/jarabe/frame/frame.py
+index 83bff06..4366076 100644
+--- a/src/jarabe/frame/frame.py
++++ b/src/jarabe/frame/frame.py
+@@ -374,22 +374,23 @@ class Frame(object):
+
+ button = self._notif_by_message.get(corner, None)
+ if button is None:
+- button = NotificationButton(xo_color)
++ button = NotificationButton(_DEFAULT_ICON, xo_color)
+ button.show()
+ self._add_message_button(button, corner)
+ self._notif_by_message[corner] = button
+
+- button.start_pulsing()
+-
+ palette = button.get_palette()
+ if palette is None:
+ palette = HistoryPalette()
+ palette.set_group_id('frame')
+ palette.connect('clear-messages', self.remove_message, corner)
++ palette.connect('notice-messages', button.stop_pulsing)
+ button.set_palette(palette)
+
++ button.start_pulsing()
++
+ palette.push_message(body, summary, icon_name, xo_color)
+- self._launch_notification_icon(icon_name, xo_color, corner, duration)
++ self._launch_notification_icon(_DEFAULT_ICON, xo_color, corner, duration)
+
+
+ def remove_message(self, palette, corner):
+diff --git a/src/jarabe/frame/notification.py b/src/jarabe/frame/notification.py
+index 34b7c1e..51e405d 100644
+--- a/src/jarabe/frame/notification.py
++++ b/src/jarabe/frame/notification.py
+@@ -38,17 +38,18 @@ _PULSE_TIMEOUT = 3
+ _PULSE_COLOR = XoColor('%s,%s' % \
+ (style.COLOR_BUTTON_GREY.get_svg(), style.COLOR_TRANSPARENT.get_svg()))
+ _BODY_FILTERS = "<img.*?/>"
+-_NOTIFICATION_ICON = 'emblem-notification'
+
+
+-def _create_pulsing_icon(icon_name, xo_color):
++def _create_pulsing_icon(icon_name, xo_color, timeout=None):
+ icon = PulsingIcon(
+ pixel_size=style.STANDARD_ICON_SIZE,
+ pulse_color=_PULSE_COLOR,
+- base_color=xo_color,
+- timeout=_PULSE_TIMEOUT,
++ base_color=xo_color
+ )
+
++ if timeout is not None:
++ icon.timeout = timeout
++
+ if icon_name.startswith(os.sep):
+ icon.props.file = icon_name
+ else:
+@@ -60,7 +61,7 @@ class _HistoryIconWidget(gtk.Alignment):
+ __gtype_name__ = 'SugarHistoryIconWidget'
+
+ def __init__(self, icon_name, xo_color):
+- icon = _create_pulsing_icon(icon_name, xo_color)
++ icon = _create_pulsing_icon(icon_name, xo_color, _PULSE_TIMEOUT)
+ icon.props.pulsing = True
+
+ gtk.Alignment.__init__(self, xalign=0.5, yalign=0.0)
+@@ -153,7 +154,8 @@ class HistoryPalette(Palette):
+ __gtype_name__ = 'SugarHistoryPalette'
+
+ __gsignals__ = {
+- 'clear-messages': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
++ 'clear-messages': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
++ 'notice-messages': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([]))
+ }
+
+ def __init__(self):
+@@ -176,17 +178,22 @@ class HistoryPalette(Palette):
+
+ self.menu.append(clear_option)
+
++ self.connect('popup', self.__notice_messages_cb)
++
+ def __clear_messages_cb(self, clear_option):
+ self.emit('clear-messages')
+
++ def __notice_messages_cb(self, palette):
++ self.emit('notice-messages')
++
+ def push_message(self, body, summary, icon_name, xo_color):
+ self._messages_box.push_message(body, summary, icon_name, xo_color)
+
+ class NotificationButton(ToolButton):
+
+- def __init__(self, xo_color):
++ def __init__(self, icon_name, xo_color):
+ ToolButton.__init__(self)
+- self._icon = _create_pulsing_icon(_NOTIFICATION_ICON, xo_color)
++ self._icon = _create_pulsing_icon(icon_name, xo_color)
+ self.set_icon_widget(self._icon)
+ self._icon.show()
+ self.set_palette_invoker(FrameWidgetInvoker(self))
+@@ -194,6 +201,9 @@ class NotificationButton(ToolButton):
+ def start_pulsing(self):
+ self._icon.props.pulsing = True
+
++ def stop_pulsing(self, widget):
++ self._icon.props.pulsing = False
++
+ class NotificationIcon(gtk.EventBox):
+ __gtype_name__ = 'SugarNotificationIcon'
+
+--
+1.7.1
+
diff --git a/rpms/sugar/0003-Yum-updater-notifications-integration.patch b/rpms/sugar/0003-Yum-updater-notifications-integration.patch
new file mode 100644
index 0000000..61d7b80
--- /dev/null
+++ b/rpms/sugar/0003-Yum-updater-notifications-integration.patch
@@ -0,0 +1,78 @@
+From 73ea63c05b786dffa97639091f0187cb663a0809 Mon Sep 17 00:00:00 2001
+From: Aleksey Lim <alsroot@member.fsf.org>
+Date: Mon, 20 Dec 2010 16:36:05 -0300
+Subject: [PATCH] Yum-updater notifications integration
+
+Original-code: http://wiki.sugarlabs.org/go/Dextrose/Updater
+dextrose-port-by: Martin Abente <martin.abente.lahaye@gmail.com>
+
+---
+ src/jarabe/desktop/homewindow.py | 32 ++++++++++++++++++++++++++++++++
+ 1 files changed, 32 insertions(+), 0 deletions(-)
+
+diff --git a/src/jarabe/desktop/homewindow.py b/src/jarabe/desktop/homewindow.py
+index d830ed0..bd5daf7 100644
+--- a/src/jarabe/desktop/homewindow.py
++++ b/src/jarabe/desktop/homewindow.py
+@@ -17,6 +17,8 @@
+ import logging
+
+ import gtk
++import dbus
++from gettext import gettext as _
+
+ from sugar.graphics import style
+ from sugar.graphics import palettegroup
+@@ -27,12 +29,19 @@ from jarabe.desktop.groupbox import GroupBox
+ from jarabe.desktop.transitionbox import TransitionBox
+ from jarabe.model.shell import ShellModel
+ from jarabe.model import shell
++from jarabe.model import notifications
+
+ _HOME_PAGE = 0
+ _GROUP_PAGE = 1
+ _MESH_PAGE = 2
+ _TRANSITION_PAGE = 3
+
++_DBUS_SYSTEM_IFACE = 'org.sugarlabs.system'
++_DBUS_SYSTEM_PATH = '/org/sugarlabs/system'
++_SYSTEM_REBOOT_ID = -1
++_SYSTEM_RELOGIN_ID = -2
++_SYSTEM_TIMEOUT = 5
++
+ class HomeWindow(gtk.Window):
+ def __init__(self):
+ logging.debug('STARTUP: Loading the desktop window')
+@@ -72,6 +81,29 @@ class HomeWindow(gtk.Window):
+ shell.get_model().zoom_level_changed.connect(
+ self.__zoom_level_changed_cb)
+
++ systembus = dbus.SystemBus()
++ systembus.add_signal_receiver(self.__reboot_cb, 'Reboot',
++ _DBUS_SYSTEM_IFACE)
++ systembus.add_signal_receiver(self.__relogin_cb, 'Relogin',
++ _DBUS_SYSTEM_IFACE)
++
++ def _system_alert(self, replaces_id, app_icon, message):
++ service = notifications.get_service()
++ service.notification_received.send(self,app_name='system',
++ replaces_id=replaces_id, app_icon=app_icon,
++ summary=_('System alert'), body=message,
++ actions=[], hints={})
++
++ def __reboot_cb(self):
++ self._system_alert(_SYSTEM_REBOOT_ID, 'system-restart',
++ _('Please, reboot your computer to take into account ' \
++ 'new updates'))
++
++ def __relogin_cb(self):
++ self._system_alert(_SYSTEM_RELOGIN_ID, 'system-logout',
++ _('Please, restart Sugar to take into account ' \
++ 'new updates'))
++
+ def _deactivate_view(self, level):
+ group = palettegroup.get_group("default")
+ group.popdown()
+--
+1.7.1
+
diff --git a/rpms/sugar/sugar.spec b/rpms/sugar/sugar.spec
index c54e9d5..4b9df0b 100644
--- a/rpms/sugar/sugar.spec
+++ b/rpms/sugar/sugar.spec
@@ -3,7 +3,7 @@
Summary: Constructionist learning platform
Name: sugar
Version: 0.88.1
-Release: 5.41dxo%{?dist}
+Release: 5.42dxo%{?dist}
URL: http://sugarlabs.org/
Source0: http://download.sugarlabs.org/sources/sucrose/glucose/%{name}/%{name}-%{version}.tar.bz2
@@ -92,6 +92,11 @@ Patch804: accessibility_0009_cp_show-virtualkeyboard-for-accessibility-traslate.
Patch901: add-button-frame.patch
+#Notifications
+Patch1001: 0001-Simple-messages-notification-extension.patch
+Patch1002: 0002-Improve-message-notification-behaviour.patch
+Patch1003: 0003-Yum-updater-notifications-integration.patch
+
License: GPLv2+
Group: User Interface/Desktops
Buildroot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
@@ -235,6 +240,10 @@ multiple instances of sugar.
%patch901 -p1
+%patch1001 -p1
+%patch1002 -p1
+%patch1003 -p1
+
%build
autoreconf
%configure