diff options
author | Martin 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) |
commit | 84d7fc8cac35313cb3d70374b6e2d8d6e2187203 (patch) | |
tree | 07d239dfb186fd21128d50ebe7e88f57fc59fc70 | |
parent | 28244016560b53dd8d7227867415f09484c673cc (diff) |
Sugar: Notification system integration
Signed-off-by: Anish Mangal <anish@sugarlabs.org>
-rw-r--r-- | rpms/sugar/0001-Simple-messages-notification-extension.patch | 578 | ||||
-rw-r--r-- | rpms/sugar/0002-Improve-message-notification-behaviour.patch | 131 | ||||
-rw-r--r-- | rpms/sugar/0003-Yum-updater-notifications-integration.patch | 78 | ||||
-rw-r--r-- | rpms/sugar/sugar.spec | 11 |
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 |