# -*- coding: utf-8 -*-
# This file is part of emesene.
#
# Emesene is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# emesene 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with emesene; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gtk
import gtk.gdk
import cairo
import gobject
class FancyAvatarRenderer(gtk.GenericCellRenderer):
"""Renderer for avatar """
__gproperties__ = {
'pixbuf': (gtk.gdk.Pixbuf, 'Pixbuf', '', gobject.PARAM_READWRITE),
'blocked': (bool, 'Contact Blocked', '', False, gobject.PARAM_READWRITE),
'dimention': (gobject.TYPE_INT, 'cell dimentions',
'height width of cell', 0, 96, 32, gobject.PARAM_READWRITE),
'status': (str, 'Contact status', '', 'FLN', gobject.PARAM_READWRITE),
'radius_factor': (gobject.TYPE_FLOAT,'radius of pixbuf',
'0.0 to 0.5 with 0.1 = 10% of dimention',
0.0, 0.5,0.11, gobject.PARAM_READWRITE),
}
def __init__(self, controller, cellDimention = 32, cellRadius = 0.11):
#gobject.GObject._init_(self)
self.__gobject_init__()
self._pixbuf = None
self._status = 'FLN'
self._blocked = False
self._dimention = cellDimention
self._radius_factor = cellRadius
#icon source used to render grayed out offline avatar
self._icon_source = gtk.IconSource()
self._icon_source.set_state(gtk.STATE_INSENSITIVE)
self.set_property('xpad', 1)
self.set_property('ypad', 1)
self._theme = controller.theme
self._config = controller.config
#set up information of statusTransformation
self._set_transformation(self._config.user['statusTransformation'])
self.transId = self._config.connect('change::statusTransformation', \
self._transformation_callback)
def destroy(self):
self._config.disconnect(self.transId)
gtk.GenericCellRenderer.destroy(self)
def _get_padding(self):
return (self.get_property('xpad'), self.get_property('ypad'))
def _set_transformation(self, setting):
transformation = setting.split('|')
self._pixalated = ('pixelate' in transformation)
self._corner = ('corner' in transformation)
self._alpha_status = ('alpha' in transformation)
self._mini = ('mini' in transformation)
self._gray = ('gray' in transformation)
def _transformation_callback(self, config, newvalue, oldvalue):
self._set_transformation(newvalue)
def do_get_property(self, property):
if property.name == 'pixbuf':
return self._pixbuf
elif property.name == 'dimention':
return self._dimention
elif property.name == 'radius-factor':
return self._radius_factor
elif property.name == 'blocked':
return self._blocked
elif property.name == 'status':
return self._status
else:
raise AttributeError, 'unknown property %s' % property.name
def do_set_property(self, property, value):
if property.name == 'pixbuf':
self._pixbuf = value
elif property.name == 'dimention':
self._dimention = value
elif property.name == 'radius-factor':
self._radius_factor = value
elif property.name == 'blocked':
self._blocked = value
elif property.name == 'status':
self._status = value
else:
raise AttributeError, 'unknown property %s' % property.name
def on_get_size(self, widget, cell_area=None):
"""Requisition size"""
xpad, ypad = self._get_padding()
if self._dimention >= 32: width = self._dimention
elif self._mini: width = self._dimention
elif self._corner: width = self._dimention * 2
else: width = self._dimention
height = self._dimention + (ypad * 2)
return (0, 0, width, height)
def on_render(self, window, widget, bg_area, cell_area, expose_area, flags):
"""Prepare rendering setting for avatar"""
xpad, ypad = self._get_padding()
x, y, width, height = cell_area
ctx = window.cairo_create()
ctx.translate(x, y)
avatar = self._pixbuf
overlay = None
alpha = 1
dim = self._dimention
if self._pixalated:
avatar = self._get_pixalate(self._pixbuf)
if self._corner:
overlay = self._get_overlay()
if self._alpha_status and self._status in ('IDL', 'FLN'):
alpha = 0.75
if self._gray and self._status == 'FLN' and avatar != None:
alpha = 1
source = self._icon_source
source.set_pixbuf(avatar)
direction = widget.get_direction()
avatar = widget.style.render_icon(source, direction,
gtk.STATE_INSENSITIVE,
-1, widget, "gtk-image")
if avatar:
self._draw_avatar(ctx, avatar, width - dim, ypad, dim,
gtk.ANCHOR_CENTER, self._radius_factor, alpha)
if overlay:
if self._dimention >= 32 :
self._draw_avatar(ctx, overlay, width - 16,
ypad + dim - 16, 16, gtk.ANCHOR_SW)
elif self._mini:
self._draw_avatar(ctx, overlay, width - 8,
ypad + dim - 8, 8, gtk.ANCHOR_SW)
else:
self._draw_avatar(ctx, overlay, 0, ypad, 16)
def _get_overlay(self):
"""Return overlay pixbuf,
dependant on contacts status and block state"""
overlay = None
if self._blocked:
overlay = self._theme.getImage('status-blocked')
elif self._status in ('AWY', 'BRB', 'LUN', 'IDL'):
overlay = self._theme.getImage('status-away')
elif self._status in ('BSY','PHN'):
overlay = self._theme.getImage('status-busy')
return overlay
def _get_pixalate(self, pixbuf):
"""Pixalate and saturate values based on original renderer
kept to retain compatibility"""
pixalate = pixbuf.copy()
if not self._status == 'NLN':
if contact.status == 'BSY':
pixalate.saturate_and_pixelate(pixbuf, 1.0, True)
else:
pixalate.saturate_and_pixelate(pixbuf, 0.1, False)
return pixalate
def _draw_avatar(self, ctx, pixbuf, x, y, dimention,
position = gtk.ANCHOR_CENTER,
radius = 0, alpha = 1):
"""Render avatar"""
ctx.save()
ctx.set_antialias(cairo.ANTIALIAS_SUBPIXEL)
ctx.translate(x, y)
pix_width = pixbuf.get_width()
pix_height = pixbuf.get_height()
if (pix_width > dimention) or (pix_height > dimention):
scale_factor = float(dimention) / max (pix_width,pix_height)
else:
scale_factor = 1
scale_width = pix_width* scale_factor
scale_height = pix_height* scale_factor
#tranlate position
if position in (gtk.ANCHOR_NW, gtk.ANCHOR_W, gtk.ANCHOR_SW):
x = 0
elif position in (gtk.ANCHOR_N, gtk.ANCHOR_CENTER, gtk.ANCHOR_S):
x = (dimention/2) - (scale_width/2)
else:
x = dimention - scale_width
if position in (gtk.ANCHOR_NW, gtk.ANCHOR_N, gtk.ANCHOR_NE):
y = 0
elif position in (gtk.ANCHOR_E, gtk.ANCHOR_CENTER, gtk.ANCHOR_W):
y = (dimention/2) - (scale_height/2)
else:
y = dimention - scale_height
ctx.translate(x, y)
if radius > 0 :
self._rounded_rectangle(ctx, 0, 0, scale_width, scale_height,
self._dimention * radius)
ctx.clip()
ctx.scale(scale_factor,scale_factor)
ctx.set_source_pixbuf(pixbuf, 0, 0)
ctx.paint_with_alpha(alpha)
ctx.restore()
def _rounded_rectangle(self, cr, x, y, w, h, radius=5):
"""Create rounded rectangle path"""
# http://cairographics.org/cookbook/roundedrectangles/
# modified from mono moonlight aka mono silverlight
# test limits (without using multiplications)
# http://graphics.stanford.edu/courses/cs248-98-fall/Final/q1.html
ARC_TO_BEZIER = 0.55228475
if radius > (min(w,h)/2):
radius = (min(w,h)/2)
#approximate (quite close) the arc using a bezier curve
c = ARC_TO_BEZIER * radius
cr.new_path();
cr.move_to ( x + radius, y)
cr.rel_line_to ( w - 2 * radius, 0.0)
cr.rel_curve_to ( c, 0.0, radius, c, radius, radius)
cr.rel_line_to ( 0, h - 2 * radius)
cr.rel_curve_to ( 0.0, c, c - radius, radius, -radius, radius)
cr.rel_line_to ( -w + 2 * radius, 0)
cr.rel_curve_to ( -c, 0, -radius, -c, -radius, -radius)
cr.rel_line_to (0, -h + 2 * radius)
cr.rel_curve_to (0.0, -c, radius - c, -radius, radius, -radius)
cr.close_path ()
gobject.type_register(FancyAvatarRenderer)
|