1
# -*- coding: utf-8 -*-
3
# This file is part of emesene.
5
# emesene is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 3 of the License, or
8
# (at your option) any later version.
10
# emesene is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with emesene; if not, write to the Free Software
17
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26
class Avatar(gtk.Widget):
28
#TODO move in an avatarManager class?????
30
#cellKeyPosition, is a Anchor Type constant
31
#refer to http://www.pygtk.org/docs/pygtk/gtk-constants.html#gtk-anchor-type-constants
33
'pixbuf': (gtk.gdk.Pixbuf, 'Pixbuf',
34
'Pixbuf',gobject.PARAM_READWRITE),
35
'pixbuf_animation': (gtk.gdk.Pixbuf, 'Pixbuf',
36
'Pixbuf from animation',gobject.PARAM_READWRITE),
37
'crossFade': (bool,'animate by crossfade if true','',True, gobject.PARAM_READWRITE),
38
'dimention': (gobject.TYPE_FLOAT,'cell dimensions',
39
'height width of cell',0.0, 128.0,32.0, gobject.PARAM_READWRITE),
40
'radius_factor': (gobject.TYPE_FLOAT,'radius of pixbuf',
41
'0.0 to 0.5 with 0.1 = 10% of dimention',0.0, 0.5,0.11, gobject.PARAM_READWRITE),
42
'keyPositin': (object,'position of avatar',
43
'corresponds to position of key in numpad', gobject.PARAM_READWRITE),
46
__gsignals__ = { 'size_request': 'override', 'expose-event': 'override' }
48
def __init__(self, cellDimention = 96, crossFade = True, cellRadius = 0.05,
49
cellKeyPosition = gtk.ANCHOR_CENTER):
50
gobject.GObject.__init__(self)
51
gtk.Widget.__init__(self)
52
self.set_flags(self.flags() | gtk.NO_WINDOW)
56
self._dimention = cellDimention
57
self._radius_factor = cellRadius
58
self._keyPosition = cellKeyPosition
61
# variables related to animation
62
self._crossFade = crossFade
63
self.inAnimation = False
64
self.duration = 1500 # milliseconds
65
self.fps = 24 # frames per second
68
self.transitionPixbuf = None
69
self.anim_source = None
70
self.current_animation = None
72
def do_get_property(self, property):
73
if property.name == 'pixbuf':
75
if property.name == 'pixbuf-animation':
77
elif property.name == 'dimention':
78
return self._dimention
79
elif property.name == 'radius-factor':
80
return self._radius_factor
81
elif property.name == 'keyPosition':
82
return self._keyPosition
83
elif property.name == 'crossFade':
84
return self._crossFade
86
raise AttributeError, 'unknown property %s' % property.name
88
def do_set_property(self, property, value):
89
if property.name == 'pixbuf':
90
if self.__shouldReplace(value):
91
if self._crossFade and not (self._pixbuf == None) and not (value == None):
92
self.transitionPixbuf = value
95
self.fps = 24 # reset fps if not valid fps
97
timeBetweenFrames = 1000 / self.fps
98
self.totalFrames = self.duration / timeBetweenFrames
100
gobject.timeout_add(timeBetweenFrames, self.animate_callback)
101
self.inAnimation = True
104
elif property.name == 'pixbuf-animation':
106
elif property.name == 'dimention':
107
self._dimention = value
108
elif property.name == 'radius-factor':
109
self._radius_factor = value
110
elif property.name == 'keyPosition':
111
self._keyPosition = value
112
elif property.name == 'crossFade':
113
self._crossFade = value
115
raise AttributeError, 'unknown property %s' % property.name
117
def animate_callback(self):
118
if self.currentFrame > self.totalFrames:
119
self.inAnimation = False
120
self._pixbuf = self.transitionPixbuf
122
if self.current_animation:
123
self._start_animation(self.current_animation)
127
if self.anim_source is not None:
128
gobject.source_remove(self.anim_source)
129
self.anim_source = None
130
self.currentFrame += 1
134
def __set_from_pixbuf(self, pixbuf):
135
self.set_property('pixbuf', pixbuf)
138
def __set_from_pixbuf_animation(self, pixbuf):
139
self.set_property('pixbuf-animation', pixbuf)
145
def set_from_file(self, filename):
146
self.filename = filename
147
if not gui.gtkui.utils.file_readable(filename):
148
self.filename = gui.theme.logo
151
animation = gtk.gdk.PixbufAnimation(filename)
152
except gobject.GError:
153
animation = gtk.gdk.PixbufAnimation(gui.theme.logo)
155
if animation.is_static_image():
156
self.__set_from_pixbuf(animation.get_static_image())
157
self.current_animation = None
160
self.current_animation = animation
161
self._start_animation(animation)
163
def set_from_image(self, image):
164
if image.get_storage_type() == gtk.IMAGE_PIXBUF:
165
self.__set_from_pixbuf(image.get_pixbuf())
166
self.current_animation = None
168
elif image.get_storage_type() == gtk.IMAGE_ANIMATION:
169
self.current_animation = image.get_animation()
170
self._start_animation(image.get_animation())
173
'''stop the animation'''
174
if self.anim_source is not None:
175
gobject.source_remove(self.anim_source)
176
self.anim_source = None
178
#end of public methods
181
def _start_animation(self, animation):
182
iteran = animation.get_iter()
183
#we don't need to resize here!
184
self.__set_from_pixbuf(iteran.get_pixbuf())
186
if self.anim_source is None:
187
self.anim_source = gobject.timeout_add(iteran.get_delay_time(), self._advance, iteran)
189
def _advance(self, iteran):
191
self.__set_from_pixbuf_animation(iteran.get_pixbuf())
192
self.anim_source = gobject.timeout_add(iteran.get_delay_time(), self._advance, iteran)
195
def do_size_request(self,requisition):
196
requisition.width = self._dimention
197
requisition.height = self._dimention
199
def do_expose_event(self, evnt):
200
if not self._pixbuf: return
202
ctx = evnt.window.cairo_create()
203
cell_area = self.get_allocation()
206
self.__draw(ctx , cell_area , self._pixbuf, 1 - \
207
(float(self.currentFrame) / self.totalFrames))
208
self.__draw(ctx , cell_area , self.transitionPixbuf, \
209
(float(self.currentFrame) / self.totalFrames))
211
self.__draw(ctx , cell_area , self._pixbuf, 1)
213
def __shouldReplace(self,pixbuf):
215
if self._pixbuf and pixbuf and \
216
pixbuf.get_pixels() == self._pixbuf.get_pixels():
221
def __draw(self,ctx, cell_area, pixbuf, alpha):
223
ctx.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
224
cell_x, cell_y, cell_width, cell_height = cell_area
225
pix_width = pixbuf.get_width()
226
pix_height = pixbuf.get_height()
227
ctx.translate(cell_x, cell_y)
229
if (pix_width > self._dimention) or (pix_height > self._dimention):
230
scale_factor = float(self._dimention) / max (pix_width,pix_height)
234
scale_width = pix_width* scale_factor
235
scale_height = pix_height* scale_factor
237
self.__translate_keyPostion(ctx, self._keyPosition,cell_width,cell_height,scale_width,scale_height)
239
scale_radius = self._dimention * self._radius_factor
240
self.__roundedrecMoonlight(ctx,0,0,scale_width,scale_height, scale_radius)
243
ctx.scale(scale_factor,scale_factor)
244
ctx.set_source_pixbuf(pixbuf,0,0)
246
ctx.paint_with_alpha(alpha)
250
def __translate_keyPostion(self,cr, key, w, h ,sw, sh):
251
if key in [gtk.ANCHOR_NORTH_WEST,gtk.ANCHOR_WEST,gtk.ANCHOR_SOUTH_WEST]:
253
elif key in [gtk.ANCHOR_NORTH,gtk.ANCHOR_CENTER,gtk.ANCHOR_SOUTH]:
258
if key in [gtk.ANCHOR_NORTH_WEST,gtk.ANCHOR_NORTH,gtk.ANCHOR_NORTH_EAST]:
260
elif key in [gtk.ANCHOR_EAST,gtk.ANCHOR_CENTER,gtk.ANCHOR_WEST]:
267
def __roundedrecMoonlight(self, cr,x,y,w,h,radius=5):
268
# http://cairographics.org/cookbook/roundedrectangles/
269
# modified from mono moonlight aka mono silverlight
270
# test limits (without using multiplications)
271
# http://graphics.stanford.edu/courses/cs248-98-fall/Final/q1.html
273
ARC_TO_BEZIER = 0.55228475
274
if radius > (min(w,h)/2):
275
radius = (min(w,h)/2)
276
#approximate (quite close) the arc using a bezier curve
277
c = ARC_TO_BEZIER * radius
280
cr.move_to(x + radius, y)
281
cr.rel_line_to(w - 2 * radius, 0.0)
282
cr.rel_curve_to(c, 0.0, radius, c, radius, radius)
283
cr.rel_line_to(0, h - 2 * radius)
284
cr.rel_curve_to(0.0, c, c - radius, radius, -radius, radius)
285
cr.rel_line_to(-w + 2 * radius, 0)
286
cr.rel_curve_to(-c, 0, -radius, -c, -radius, -radius)
287
cr.rel_line_to(0, -h + 2 * radius)
288
cr.rel_curve_to(0.0, -c, radius - c, -radius, radius, -radius)
291
gobject.type_register(Avatar)