From 6272fb7acbd654ceb00d2748d4e7855adf6f02bc Mon Sep 17 00:00:00 2001
From: Martin Abente <martin.abente.lahaye@gmail.com>
Date: Tue, 21 Dec 2010 12:00:49 -0300
Subject: [PATCH 2/3] Sugar: Notification system integration
Organization: Sugar Labs Foundation


Signed-off-by: Anish Mangal <anish@sugarlabs.org>
---
 ...01-Simple-messages-notification-extension.patch |  578 ++++++++++++++++++++
 ...02-Improve-message-notification-behaviour.patch |  131 +++++
 ...003-Yum-updater-notifications-integration.patch |   78 +++
 rpms/sugar/sugar.spec                              |   11 +-
 4 files changed, 797 insertions(+), 1 deletions(-)
 create mode 100644 rpms/sugar/0001-Simple-messages-notification-extension.patch
 create mode 100644 rpms/sugar/0002-Improve-message-notification-behaviour.patch
 create mode 100644 rpms/sugar/0003-Yum-updater-notifications-integration.patch

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
-- 
1.7.3.4

