~ubuntu-branches/ubuntu/oneiric/emesene/oneiric-proposed

« back to all changes in this revision

Viewing changes to emesene/gui/gtkui/Avatar.py

  • Committer: Bazaar Package Importer
  • Author(s): Devid Antonio Filoni
  • Date: 2011-03-03 14:49:13 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20110303144913-0adl9cmw2s35lvzo
Tags: 2.0~git20110303-0ubuntu1
* New upstream git revision (LP: #728469).
* Remove debian/watch, debian/emesene.xpm, debian/install and
  debian/README.source files.
* Remove 21_svn2451_fix_avatar and 20_dont_build_own_libmimic patches.
* debian/control: modify python to python (>= 2.5) in Build-Depends field.
* debian/control: remove python-libmimic from Recommends field.
* debian/control: modify python-gtk2 (>= 2.10) to python-gtk2 (>= 2.12) in
  Depends field.
* debian/control: add python-appindicator and python-xmpp to Recommends
  field.
* debian/control: add python-papyon (>= 0.5.4) and python-webkit to Depends
  field.
* debian/control: update Description field.
* debian/control: add python-setuptools to Build-Depends field.
* debian/control: move python-dbus and python-notify to Depends field.
* Update debian/copyright file.
* Update debian/links file.
* debian/menu: update description field.
* Bump Standards-Version to 3.9.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
#    This file is part of emesene.
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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
 
18
 
 
19
import gtk
 
20
import gtk.gdk
 
21
import cairo
 
22
import gobject
 
23
 
 
24
import gui
 
25
 
 
26
class Avatar(gtk.Widget):
 
27
    """AvatarWidget """
 
28
    #TODO move in an avatarManager class?????
 
29
 
 
30
    #cellKeyPosition, is a Anchor Type constant
 
31
    #refer to http://www.pygtk.org/docs/pygtk/gtk-constants.html#gtk-anchor-type-constants
 
32
    __gproperties__ = {
 
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),
 
44
        }
 
45
 
 
46
    __gsignals__ = { 'size_request': 'override', 'expose-event': 'override' }
 
47
 
 
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)
 
53
 
 
54
        self.filename = ''
 
55
        self._pixbuf = None
 
56
        self._dimention = cellDimention
 
57
        self._radius_factor = cellRadius
 
58
        self._keyPosition = cellKeyPosition
 
59
 
 
60
 
 
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
 
66
        self.totalFrames = 0
 
67
        self.currentFrame = 0
 
68
        self.transitionPixbuf = None
 
69
        self.anim_source = None
 
70
        self.current_animation = None
 
71
 
 
72
    def do_get_property(self, property):
 
73
        if property.name == 'pixbuf':
 
74
            return self._pixbuf
 
75
        if property.name == 'pixbuf-animation':
 
76
            return self._pixbuf
 
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
 
85
        else:
 
86
            raise AttributeError, 'unknown property %s' % property.name
 
87
 
 
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
 
93
 
 
94
                    if self.fps < 1:
 
95
                        self.fps = 24 # reset fps if not valid fps
 
96
 
 
97
                    timeBetweenFrames = 1000 / self.fps
 
98
                    self.totalFrames = self.duration / timeBetweenFrames
 
99
                    self.currentFrame = 1
 
100
                    gobject.timeout_add(timeBetweenFrames, self.animate_callback)
 
101
                    self.inAnimation = True
 
102
                else:
 
103
                    self._pixbuf = value
 
104
        elif property.name == 'pixbuf-animation':
 
105
            self._pixbuf = value
 
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
 
114
        else:
 
115
            raise AttributeError, 'unknown property %s' % property.name
 
116
 
 
117
    def animate_callback(self):
 
118
        if self.currentFrame > self.totalFrames:
 
119
            self.inAnimation = False
 
120
            self._pixbuf = self.transitionPixbuf
 
121
 
 
122
            if self.current_animation:
 
123
                self._start_animation(self.current_animation)
 
124
            return False
 
125
        else:
 
126
 
 
127
            if self.anim_source is not None:
 
128
                gobject.source_remove(self.anim_source)
 
129
                self.anim_source = None
 
130
            self.currentFrame += 1
 
131
            self.queue_draw()
 
132
            return True
 
133
 
 
134
    def __set_from_pixbuf(self, pixbuf):
 
135
        self.set_property('pixbuf', pixbuf)
 
136
        self.queue_draw()
 
137
 
 
138
    def __set_from_pixbuf_animation(self, pixbuf):
 
139
        self.set_property('pixbuf-animation', pixbuf)
 
140
        self.queue_draw()
 
141
 
 
142
    #
 
143
    #public methods
 
144
    #
 
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
 
149
 
 
150
        try:
 
151
            animation = gtk.gdk.PixbufAnimation(filename)
 
152
        except gobject.GError:
 
153
            animation = gtk.gdk.PixbufAnimation(gui.theme.logo)
 
154
 
 
155
        if animation.is_static_image():
 
156
            self.__set_from_pixbuf(animation.get_static_image())
 
157
            self.current_animation = None
 
158
            return
 
159
 
 
160
        self.current_animation = animation
 
161
        self._start_animation(animation)
 
162
 
 
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
 
167
            return
 
168
        elif image.get_storage_type() == gtk.IMAGE_ANIMATION:
 
169
            self.current_animation = image.get_animation()
 
170
            self._start_animation(image.get_animation())
 
171
 
 
172
    def stop(self):
 
173
        '''stop the animation'''
 
174
        if self.anim_source is not None:
 
175
            gobject.source_remove(self.anim_source)
 
176
            self.anim_source = None
 
177
    #
 
178
    #end of public methods
 
179
    #
 
180
 
 
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())
 
185
 
 
186
        if self.anim_source is None:
 
187
            self.anim_source = gobject.timeout_add(iteran.get_delay_time(), self._advance, iteran)
 
188
 
 
189
    def _advance(self, iteran):
 
190
        iteran.advance()
 
191
        self.__set_from_pixbuf_animation(iteran.get_pixbuf())
 
192
        self.anim_source = gobject.timeout_add(iteran.get_delay_time(), self._advance, iteran)
 
193
        return False
 
194
 
 
195
    def do_size_request(self,requisition):
 
196
        requisition.width = self._dimention
 
197
        requisition.height = self._dimention
 
198
 
 
199
    def do_expose_event(self, evnt):
 
200
        if not self._pixbuf: return
 
201
 
 
202
        ctx = evnt.window.cairo_create()
 
203
        cell_area = self.get_allocation()
 
204
 
 
205
        if self.inAnimation:
 
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))
 
210
        else:
 
211
            self.__draw(ctx , cell_area , self._pixbuf, 1)
 
212
 
 
213
    def __shouldReplace(self,pixbuf):
 
214
        #checkEquivalence
 
215
        if self._pixbuf and pixbuf and \
 
216
          pixbuf.get_pixels() == self._pixbuf.get_pixels():
 
217
            return False
 
218
        else:
 
219
            return True
 
220
 
 
221
    def __draw(self,ctx, cell_area, pixbuf, alpha):
 
222
        ctx.save()
 
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)
 
228
 
 
229
        if (pix_width > self._dimention) or (pix_height > self._dimention):
 
230
            scale_factor = float(self._dimention) / max (pix_width,pix_height)
 
231
        else:
 
232
            scale_factor = 1
 
233
 
 
234
        scale_width = pix_width* scale_factor
 
235
        scale_height = pix_height* scale_factor
 
236
 
 
237
        self.__translate_keyPostion(ctx, self._keyPosition,cell_width,cell_height,scale_width,scale_height)
 
238
 
 
239
        scale_radius = self._dimention * self._radius_factor
 
240
        self.__roundedrecMoonlight(ctx,0,0,scale_width,scale_height, scale_radius)
 
241
 
 
242
        ctx.clip()
 
243
        ctx.scale(scale_factor,scale_factor)
 
244
        ctx.set_source_pixbuf(pixbuf,0,0)
 
245
 
 
246
        ctx.paint_with_alpha(alpha)
 
247
 
 
248
        ctx.restore()
 
249
 
 
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]:
 
252
            x = 0
 
253
        elif key in [gtk.ANCHOR_NORTH,gtk.ANCHOR_CENTER,gtk.ANCHOR_SOUTH]:
 
254
            x = (w/2) - (sw/2)
 
255
        else:
 
256
            x = w - sw
 
257
 
 
258
        if key in [gtk.ANCHOR_NORTH_WEST,gtk.ANCHOR_NORTH,gtk.ANCHOR_NORTH_EAST]:
 
259
            y = 0
 
260
        elif key in [gtk.ANCHOR_EAST,gtk.ANCHOR_CENTER,gtk.ANCHOR_WEST]:
 
261
            y = (h/2) - (sh/2)
 
262
        else:
 
263
            y = h - sh
 
264
 
 
265
        cr.translate(x, y)
 
266
 
 
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
 
272
 
 
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
 
278
 
 
279
        cr.new_path();
 
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)
 
289
        cr.close_path()
 
290
 
 
291
gobject.type_register(Avatar)