Merge branch 'master' of git+ssh://dev.laptop.org/git/sugar
This commit is contained in:
commit
1eb9932ab3
2
NEWS
2
NEWS
@ -1,3 +1,5 @@
|
|||||||
|
Added TimeoutAlert (erikos)
|
||||||
|
|
||||||
Snapshot 68ff71a0cb
|
Snapshot 68ff71a0cb
|
||||||
|
|
||||||
Snapshot 29bc0a8a20
|
Snapshot 29bc0a8a20
|
||||||
|
@ -1,7 +1,26 @@
|
|||||||
|
# Copyright (C) 2007, One Laptop Per Child
|
||||||
|
#
|
||||||
|
# This library is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This library is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this library; if not, write to the
|
||||||
|
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||||
|
# Boston, MA 02111-1307, USA.
|
||||||
|
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
import gtk
|
import gtk
|
||||||
import gobject
|
import gobject
|
||||||
|
import hippo
|
||||||
|
import math
|
||||||
|
|
||||||
from sugar.graphics import style
|
from sugar.graphics import style
|
||||||
from sugar.graphics.icon import Icon
|
from sugar.graphics.icon import Icon
|
||||||
@ -28,7 +47,7 @@ class Alert(gtk.EventBox, gobject.GObject):
|
|||||||
|
|
||||||
__gsignals__ = {
|
__gsignals__ = {
|
||||||
'response': (gobject.SIGNAL_RUN_FIRST,
|
'response': (gobject.SIGNAL_RUN_FIRST,
|
||||||
gobject.TYPE_NONE, ([int]))
|
gobject.TYPE_NONE, ([object]))
|
||||||
}
|
}
|
||||||
|
|
||||||
__gproperties__ = {
|
__gproperties__ = {
|
||||||
@ -69,7 +88,8 @@ class Alert(gtk.EventBox, gobject.GObject):
|
|||||||
self._hbox.pack_start(self._msg_box)
|
self._hbox.pack_start(self._msg_box)
|
||||||
|
|
||||||
self._buttons_box = gtk.HButtonBox()
|
self._buttons_box = gtk.HButtonBox()
|
||||||
self._buttons_box.set_layout(gtk.BUTTONBOX_SPREAD)
|
self._buttons_box.set_layout(gtk.BUTTONBOX_END)
|
||||||
|
self._buttons_box.set_spacing(style.DEFAULT_SPACING)
|
||||||
self._hbox.pack_start(self._buttons_box)
|
self._hbox.pack_start(self._buttons_box)
|
||||||
self._buttons_box.show()
|
self._buttons_box.show()
|
||||||
|
|
||||||
@ -99,17 +119,20 @@ class Alert(gtk.EventBox, gobject.GObject):
|
|||||||
elif pspec.name == 'msg':
|
elif pspec.name == 'msg':
|
||||||
return self._msg
|
return self._msg
|
||||||
|
|
||||||
def add_button(self, response_id, label, icon, position=-1):
|
def add_button(self, response_id, label, icon=None, position=-1):
|
||||||
"""Add a button to the alert
|
"""Add a button to the alert
|
||||||
|
|
||||||
response_id: will be emitted with the response signal
|
response_id: will be emitted with the response signal
|
||||||
|
a response ID should one of the pre-defined
|
||||||
|
GTK Response Type Constants or a positive number
|
||||||
label: that will occure right to the buttom
|
label: that will occure right to the buttom
|
||||||
icon: this can be a SugarIcon or a gtk.Image
|
icon: this can be a SugarIcon or a gtk.Image
|
||||||
position: the position of the button in the box (optional)
|
position: the position of the button in the box (optional)
|
||||||
"""
|
"""
|
||||||
button = gtk.Button()
|
button = gtk.Button()
|
||||||
self._buttons[response_id] = button
|
self._buttons[response_id] = button
|
||||||
button.set_image(icon)
|
if icon is not None:
|
||||||
|
button.set_image(icon)
|
||||||
button.set_label(label)
|
button.set_label(label)
|
||||||
self._buttons_box.pack_start(button)
|
self._buttons_box.pack_start(button)
|
||||||
button.show()
|
button.show()
|
||||||
@ -142,11 +165,66 @@ class ConfirmationAlert(Alert):
|
|||||||
Alert.__init__(self, **kwargs)
|
Alert.__init__(self, **kwargs)
|
||||||
|
|
||||||
icon = Icon(icon_name='dialog-cancel')
|
icon = Icon(icon_name='dialog-cancel')
|
||||||
cancel_button = self.add_button(0, _('Cancel'), icon)
|
cancel_button = self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||||
icon.show()
|
icon.show()
|
||||||
|
|
||||||
icon = Icon(icon_name='dialog-ok')
|
icon = Icon(icon_name='dialog-ok')
|
||||||
ok_button = self.add_button(1, _('Ok'), icon)
|
ok_button = self.add_button(gtk.RESPONSE_OK, _('Ok'), icon)
|
||||||
icon.show()
|
icon.show()
|
||||||
|
|
||||||
|
|
||||||
|
class _TimeoutIcon(hippo.CanvasText, hippo.CanvasItem):
|
||||||
|
__gtype_name__ = 'AlertTimeoutIcon'
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
hippo.CanvasText.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
self.props.orientation = hippo.ORIENTATION_HORIZONTAL
|
||||||
|
self.props.border_left = style.DEFAULT_SPACING
|
||||||
|
self.props.border_right = style.DEFAULT_SPACING
|
||||||
|
|
||||||
|
def do_paint_background(self, cr, damaged_box):
|
||||||
|
[width, height] = self.get_allocation()
|
||||||
|
|
||||||
|
x = width * 0.5
|
||||||
|
y = height * 0.5
|
||||||
|
radius = min(width * 0.5, height * 0.5)
|
||||||
|
|
||||||
|
hippo.cairo_set_source_rgba32(cr, self.props.background_color)
|
||||||
|
cr.arc(x, y, radius, 0, 2*math.pi)
|
||||||
|
cr.fill_preserve()
|
||||||
|
|
||||||
|
|
||||||
|
class TimeoutAlert(Alert):
|
||||||
|
"""This is a ready-made two button (Cancel,Continue) alert
|
||||||
|
|
||||||
|
It times out with a positive reponse after the given amount of seconds.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, timeout=5, **kwargs):
|
||||||
|
Alert.__init__(self, **kwargs)
|
||||||
|
|
||||||
|
self._timeout = timeout
|
||||||
|
|
||||||
|
icon = Icon(icon_name='dialog-cancel')
|
||||||
|
cancel_button = self.add_button(gtk.RESPONSE_CANCEL, _('Cancel'), icon)
|
||||||
|
icon.show()
|
||||||
|
|
||||||
|
self._timeout_text = _TimeoutIcon(
|
||||||
|
text=self._timeout,
|
||||||
|
color=style.COLOR_BUTTON_GREY.get_int(),
|
||||||
|
background_color=style.COLOR_WHITE.get_int())
|
||||||
|
canvas = hippo.Canvas()
|
||||||
|
canvas.set_root(self._timeout_text)
|
||||||
|
canvas.show()
|
||||||
|
self.add_button(gtk.RESPONSE_OK, _('Continue'), canvas)
|
||||||
|
|
||||||
|
gobject.timeout_add(1000, self.__timeout)
|
||||||
|
|
||||||
|
def __timeout(self):
|
||||||
|
self._timeout -= 1
|
||||||
|
self._timeout_text.props.text = self._timeout
|
||||||
|
if self._timeout == 0:
|
||||||
|
self._response(gtk.RESPONSE_OK)
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
@ -26,9 +26,8 @@ _logger = logging.getLogger('sugar.presence.activity')
|
|||||||
class Activity(gobject.GObject):
|
class Activity(gobject.GObject):
|
||||||
"""UI interface for an Activity in the presence service
|
"""UI interface for an Activity in the presence service
|
||||||
|
|
||||||
Activities in the presence service represent other user's
|
Activities in the presence service represent your and other user's
|
||||||
shared activities and your own activities (XXX shared or
|
shared activities.
|
||||||
otherwise?)
|
|
||||||
|
|
||||||
Properties:
|
Properties:
|
||||||
id
|
id
|
||||||
@ -69,8 +68,10 @@ class Activity(gobject.GObject):
|
|||||||
self._ps_del_object = del_obj_cb
|
self._ps_del_object = del_obj_cb
|
||||||
bobj = bus.get_object(self._PRESENCE_SERVICE, object_path)
|
bobj = bus.get_object(self._PRESENCE_SERVICE, object_path)
|
||||||
self._activity = dbus.Interface(bobj, self._ACTIVITY_DBUS_INTERFACE)
|
self._activity = dbus.Interface(bobj, self._ACTIVITY_DBUS_INTERFACE)
|
||||||
self._activity.connect_to_signal('BuddyJoined', self._buddy_joined_cb)
|
self._activity.connect_to_signal('BuddyHandleJoined',
|
||||||
self._activity.connect_to_signal('BuddyLeft', self._buddy_left_cb)
|
self._buddy_handle_joined_cb)
|
||||||
|
self._activity.connect_to_signal('BuddyLeft',
|
||||||
|
self._buddy_left_cb)
|
||||||
self._activity.connect_to_signal('NewChannel', self._new_channel_cb)
|
self._activity.connect_to_signal('NewChannel', self._new_channel_cb)
|
||||||
self._activity.connect_to_signal('PropertiesChanged',
|
self._activity.connect_to_signal('PropertiesChanged',
|
||||||
self._properties_changed_cb,
|
self._properties_changed_cb,
|
||||||
@ -90,6 +91,9 @@ class Activity(gobject.GObject):
|
|||||||
self._tags = None
|
self._tags = None
|
||||||
self._private = True
|
self._private = True
|
||||||
self._joined = False
|
self._joined = False
|
||||||
|
# Cache for get_buddy_by_handle, maps handles to buddy object paths
|
||||||
|
self._handle_to_buddy_path = {}
|
||||||
|
self._buddy_path_to_handle = {}
|
||||||
|
|
||||||
def _get_properties_reply_cb(self, new_props):
|
def _get_properties_reply_cb(self, new_props):
|
||||||
self._properties_changed_cb(new_props)
|
self._properties_changed_cb(new_props)
|
||||||
@ -178,8 +182,10 @@ class Activity(gobject.GObject):
|
|||||||
self.emit('buddy-joined', self._ps_new_object(object_path))
|
self.emit('buddy-joined', self._ps_new_object(object_path))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def _buddy_joined_cb(self, object_path):
|
def _buddy_handle_joined_cb(self, object_path, handle):
|
||||||
gobject.idle_add(self._emit_buddy_joined_signal, object_path)
|
gobject.idle_add(self._emit_buddy_joined_signal, object_path)
|
||||||
|
self._handle_to_buddy_path[handle] = object_path
|
||||||
|
self._buddy_path_to_handle[object_path] = handle
|
||||||
|
|
||||||
def _emit_buddy_left_signal(self, object_path):
|
def _emit_buddy_left_signal(self, object_path):
|
||||||
"""Generate buddy-left GObject signal with presence Buddy object
|
"""Generate buddy-left GObject signal with presence Buddy object
|
||||||
@ -191,6 +197,8 @@ class Activity(gobject.GObject):
|
|||||||
|
|
||||||
def _buddy_left_cb(self, object_path):
|
def _buddy_left_cb(self, object_path):
|
||||||
gobject.idle_add(self._emit_buddy_left_signal, object_path)
|
gobject.idle_add(self._emit_buddy_left_signal, object_path)
|
||||||
|
handle = self._buddy_path_to_handle.pop(object_path)
|
||||||
|
self._handle_to_buddy_path.pop(handle, None)
|
||||||
|
|
||||||
def _emit_new_channel_signal(self, object_path):
|
def _emit_new_channel_signal(self, object_path):
|
||||||
"""Generate new-channel GObject signal with channel object path
|
"""Generate new-channel GObject signal with channel object path
|
||||||
@ -214,6 +222,18 @@ class Activity(gobject.GObject):
|
|||||||
buddies.append(self._ps_new_object(item))
|
buddies.append(self._ps_new_object(item))
|
||||||
return buddies
|
return buddies
|
||||||
|
|
||||||
|
def get_buddy_by_handle(self, handle):
|
||||||
|
"""Retrieve the Buddy object given a telepathy handle.
|
||||||
|
|
||||||
|
buddy object paths are cached in self._handle_to_buddy_path,
|
||||||
|
so we can get the buddy without calling PS.
|
||||||
|
"""
|
||||||
|
object_path = self._handle_to_buddy_path.get(handle, None)
|
||||||
|
if object_path:
|
||||||
|
buddy = self._ps_new_object(object_path)
|
||||||
|
return buddy
|
||||||
|
return None
|
||||||
|
|
||||||
def invite(self, buddy, message, response_cb):
|
def invite(self, buddy, message, response_cb):
|
||||||
"""Invite the given buddy to join this activity.
|
"""Invite the given buddy to join this activity.
|
||||||
|
|
||||||
@ -244,9 +264,13 @@ class Activity(gobject.GObject):
|
|||||||
def get_channels(self):
|
def get_channels(self):
|
||||||
"""Retrieve communications channel descriptions for the activity
|
"""Retrieve communications channel descriptions for the activity
|
||||||
|
|
||||||
Returns (bus name, connection, channels) for the activity
|
Returns a tuple containing:
|
||||||
|
- the D-Bus well-known service name of the connection
|
||||||
XXX what are those values?
|
(FIXME: this is redundant; in Telepathy it can be derived
|
||||||
|
from that of the connection)
|
||||||
|
- the D-Bus object path of the connection
|
||||||
|
- a list of D-Bus object paths representing the channels
|
||||||
|
associated with this activity
|
||||||
"""
|
"""
|
||||||
(bus_name, connection, channels) = self._activity.GetChannels()
|
(bus_name, connection, channels) = self._activity.GetChannels()
|
||||||
return bus_name, connection, channels
|
return bus_name, connection, channels
|
||||||
@ -256,10 +280,8 @@ class Activity(gobject.GObject):
|
|||||||
self.emit("joined", False, "left activity")
|
self.emit("joined", False, "left activity")
|
||||||
|
|
||||||
def _leave_error_cb(self, err):
|
def _leave_error_cb(self, err):
|
||||||
"""Callback for error in async leaving of shared activity.
|
"""Callback for error in async leaving of shared activity."""
|
||||||
|
_logger.debug('Failed to leave activity: %s', err)
|
||||||
XXX Add logging!"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
def leave(self):
|
def leave(self):
|
||||||
"""Leave this shared activity"""
|
"""Leave this shared activity"""
|
||||||
|
Loading…
Reference in New Issue
Block a user