Update copyright info from 2005-2008 to 2005-2009
[gpodder.git] / src / gpodder / draw.py
blobf4c6e7af4c7ceb1e29d3425b57c12b858dc92524
1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
22 # draw.py -- Draw routines for gPodder-specific graphics
23 # Thomas Perl <thp@perli.net>, 2007-11-25
27 import gtk
28 import pango
29 import cairo
30 import StringIO
33 class TextExtents(object):
34 def __init__(self, ctx, text):
35 tuple = ctx.text_extents(text)
36 (self.x_bearing, self.y_bearing, self.width, self.height, self.x_advance, self.y_advance) = tuple
39 RRECT_LEFT_SIDE = 1
40 RRECT_RIGHT_SIDE = 2
42 def draw_rounded_rectangle(ctx, x, y, w, h, r=10, left_side_width = None, sides_to_draw=0, close=False):
43 if left_side_width is None:
44 left_side_width = flw/2
46 x = int(x)
47 offset = 0
48 if close: offset = 0.5
50 if sides_to_draw & RRECT_LEFT_SIDE:
51 ctx.move_to(x+int(left_side_width)-offset, y+h)
52 ctx.line_to(x+r, y+h)
53 ctx.curve_to(x, y+h, x, y+h, x, y+h-r)
54 ctx.line_to(x, y+r)
55 ctx.curve_to(x, y, x, y, x+r, y)
56 ctx.line_to(x+int(left_side_width)-offset, y)
57 if close:
58 ctx.line_to(x+int(left_side_width)-offset, y+h)
60 if sides_to_draw & RRECT_RIGHT_SIDE:
61 ctx.move_to(x+int(left_side_width)+offset, y)
62 ctx.line_to(x+w-r, y)
63 ctx.curve_to(x+w, y, x+w, y, x+w, y+r)
64 ctx.line_to(x+w, y+h-r)
65 ctx.curve_to(x+w, y+h, x+w, y+h, x+w-r, y+h)
66 ctx.line_to(x+int(left_side_width)+offset, y+h)
67 if close:
68 ctx.line_to(x+int(left_side_width)+offset, y)
71 def draw_text_pill(left_text, right_text, x=0, y=0, border=4, radius=14):
72 # Create temporary context to calculate the text size
73 ctx = cairo.Context(cairo.ImageSurface(cairo.FORMAT_ARGB32, 1, 1))
75 # Use GTK+ style of a normal Button
76 widget = gtk.ProgressBar()
77 style = widget.rc_get_style()
79 x_border = int(border*1.2)
81 font_desc = style.font_desc
82 font_size = float(1.15*font_desc.get_size())/float(pango.SCALE)
83 font_name = font_desc.get_family()
85 ctx.set_font_size(font_size)
86 ctx.select_font_face(font_name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
88 left_text_e = TextExtents(ctx, left_text)
89 right_text_e = TextExtents(ctx, right_text)
90 text_height = max(left_text_e.height, right_text_e.height)
92 image_height = int(y+text_height+border*2)
93 image_width = int(x+left_text_e.width+right_text_e.width+x_border*4)
94 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, image_width, image_height)
96 ctx = cairo.Context(surface)
97 ctx.set_font_size(font_size)
98 ctx.select_font_face(font_name, cairo.FONT_SLANT_NORMAL, cairo.FONT_WEIGHT_BOLD)
100 if left_text == '0':
101 left_text = None
102 if right_text == '0':
103 right_text = None
105 left_side_width = left_text_e.width + x_border*2
106 right_side_width = right_text_e.width + x_border*2
108 rect_width = left_side_width + right_side_width
109 rect_height = text_height + border*2
110 if left_text is not None:
111 draw_rounded_rectangle(ctx,x,y,rect_width,rect_height,radius, left_side_width, RRECT_LEFT_SIDE, right_text is None)
112 linear = cairo.LinearGradient(x, y, x+left_side_width/2, y+rect_height/2)
113 linear.add_color_stop_rgba(0, .7, .7, .7, .5)
114 linear.add_color_stop_rgba(1, .4, .4, .4, .5)
115 ctx.set_source(linear)
116 ctx.fill_preserve()
117 ctx.set_source_rgba(0, 0, 0, .4)
118 ctx.set_line_width(1)
119 ctx.stroke()
121 ctx.move_to(x+1+x_border-left_text_e.x_bearing, y+1+border+text_height)
122 ctx.set_source_rgba( 0, 0, 0, 1)
123 ctx.show_text(left_text)
124 ctx.move_to(x+x_border-left_text_e.x_bearing, y+border+text_height)
125 ctx.set_source_rgba( 1, 1, 1, 1)
126 ctx.show_text(left_text)
128 if right_text is not None:
129 draw_rounded_rectangle(ctx, x, y, rect_width, rect_height, radius, left_side_width, RRECT_RIGHT_SIDE, left_text is None)
130 linear = cairo.LinearGradient(x+left_side_width, y, x+left_side_width+right_side_width/2, y+rect_height)
131 linear.add_color_stop_rgba(0, 0, 0, 0, .9)
132 linear.add_color_stop_rgba(1, 0, 0, 0, .5)
133 ctx.set_source(linear)
134 ctx.fill_preserve()
135 ctx.set_source_rgba(0, 0, 0, .7)
136 ctx.set_line_width(1)
137 ctx.stroke()
139 ctx.move_to(x+1+x_border*3+left_text_e.width-right_text_e.x_bearing, y+1+border+text_height)
140 ctx.set_source_rgba( 0, 0, 0, 1)
141 ctx.show_text(right_text)
142 ctx.move_to(x+x_border*3+left_text_e.width-right_text_e.x_bearing, y+border+text_height)
143 ctx.set_source_rgba( 1, 1, 1, 1)
144 ctx.show_text(right_text)
146 return surface
149 def draw_pill_pixbuf(left_text, right_text):
150 return cairo_surface_to_pixbuf(draw_text_pill(left_text, right_text))
153 def cairo_surface_to_pixbuf(s):
155 Converts a Cairo surface to a Gtk Pixbuf by
156 encoding it as PNG and using the PixbufLoader.
158 sio = StringIO.StringIO()
159 try:
160 s.write_to_png(sio)
161 except:
162 # Write an empty PNG file to the StringIO, so
163 # in case of an error we have "something" to
164 # load. This happens in PyCairo < 1.1.6, see:
165 # http://webcvs.cairographics.org/pycairo/NEWS?view=markup
166 # Thanks to Chris Arnold for reporting this bug
167 sio.write('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9cMEQkqIyxn3RkAAAAZdEVYdENv\nbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJ\nRU5ErkJggg==\n'.decode('base64'))
169 pbl = gtk.gdk.PixbufLoader()
170 pbl.write(sio.getvalue())
171 pbl.close()
173 pixbuf = pbl.get_pixbuf()
174 return pixbuf
177 def progressbar_pixbuf(width, height, percentage):
178 COLOR_BG = (.4, .4, .4, .4)
179 COLOR_FG = (.2, .9, .2, 1.)
180 COLOR_FG_HIGH = (1., 1., 1., .5)
181 COLOR_BORDER = (0., 0., 0., 1.)
183 surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
184 ctx = cairo.Context(surface)
186 padding = int(float(width)/8.0)
187 bar_width = 2*padding
188 bar_height = height - 2*padding
189 bar_height_fill = bar_height*percentage
191 # Background
192 ctx.rectangle(padding, padding, bar_width, bar_height)
193 ctx.set_source_rgba(*COLOR_BG)
194 ctx.fill()
196 # Foreground
197 ctx.rectangle(padding, padding+bar_height-bar_height_fill, bar_width, bar_height_fill)
198 ctx.set_source_rgba(*COLOR_FG)
199 ctx.fill()
200 ctx.rectangle(padding+bar_width/3, padding+bar_height-bar_height_fill, bar_width/4, bar_height_fill)
201 ctx.set_source_rgba(*COLOR_FG_HIGH)
202 ctx.fill()
204 # Border
205 ctx.rectangle(padding-.5, padding-.5, bar_width+1, bar_height+1)
206 ctx.set_source_rgba(*COLOR_BORDER)
207 ctx.set_line_width(1.)
208 ctx.stroke()
210 return cairo_surface_to_pixbuf(surface)