1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2010 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
36 class TextExtents(object):
37 def __init__(self
, ctx
, text
):
38 tuple = ctx
.text_extents(text
)
39 (self
.x_bearing
, self
.y_bearing
, self
.width
, self
.height
, self
.x_advance
, self
.y_advance
) = tuple
45 def draw_rounded_rectangle(ctx
, x
, y
, w
, h
, r
=10, left_side_width
= None, sides_to_draw
=0, close
=False):
46 if left_side_width
is None:
47 left_side_width
= flw
/2
51 if close
: offset
= 0.5
53 if sides_to_draw
& RRECT_LEFT_SIDE
:
54 ctx
.move_to(x
+int(left_side_width
)-offset
, y
+h
)
56 ctx
.curve_to(x
, y
+h
, x
, y
+h
, x
, y
+h
-r
)
58 ctx
.curve_to(x
, y
, x
, y
, x
+r
, y
)
59 ctx
.line_to(x
+int(left_side_width
)-offset
, y
)
61 ctx
.line_to(x
+int(left_side_width
)-offset
, y
+h
)
63 if sides_to_draw
& RRECT_RIGHT_SIDE
:
64 ctx
.move_to(x
+int(left_side_width
)+offset
, y
)
66 ctx
.curve_to(x
+w
, y
, x
+w
, y
, x
+w
, y
+r
)
67 ctx
.line_to(x
+w
, y
+h
-r
)
68 ctx
.curve_to(x
+w
, y
+h
, x
+w
, y
+h
, x
+w
-r
, y
+h
)
69 ctx
.line_to(x
+int(left_side_width
)+offset
, y
+h
)
71 ctx
.line_to(x
+int(left_side_width
)+offset
, y
)
74 def rounded_rectangle(ctx
, x
, y
, width
, height
, radius
=4.):
75 """Simple rounded rectangle algorithmn
77 http://www.cairographics.org/samples/rounded_rectangle/
79 degrees
= math
.pi
/ 180.
82 ctx
.arc(x
+ width
- radius
, y
+ radius
, radius
, -90. * degrees
, 0)
83 ctx
.arc(x
+ width
- radius
, y
+ height
- radius
, radius
, 0, 90. * degrees
)
84 ctx
.arc(x
+ radius
, y
+ height
- radius
, radius
, 90. * degrees
, 180. * degrees
)
85 ctx
.arc(x
+ radius
, y
+ radius
, radius
, 180. * degrees
, 270. * degrees
)
89 def draw_text_box_centered(ctx
, widget
, w_width
, w_height
, text
, font_desc
=None, add_progress
=None):
90 style
= widget
.rc_get_style()
91 text_color
= style
.text
[gtk
.STATE_PRELIGHT
]
92 red
, green
, blue
= text_color
.red
, text_color
.green
, text_color
.blue
93 text_color
= [float(x
)/65535. for x
in (red
, green
, blue
)]
97 font_desc
= style
.font_desc
98 font_desc
.set_size(14*pango
.SCALE
)
100 pango_context
= widget
.create_pango_context()
101 layout
= pango
.Layout(pango_context
)
102 layout
.set_font_description(font_desc
)
103 layout
.set_text(text
)
104 width
, height
= layout
.get_pixel_size()
106 ctx
.move_to(w_width
/2-width
/2, w_height
/2-height
/2)
107 ctx
.set_source_rgba(*text_color
)
108 ctx
.show_layout(layout
)
110 # Draw an optional progress bar below the text (same width)
111 if add_progress
is not None:
113 ctx
.set_source_rgba(*text_color
)
114 ctx
.set_line_width(1.)
115 rounded_rectangle(ctx
, w_width
/2-width
/2-.5, w_height
/2+height
-.5, width
+1, bar_height
+1)
117 rounded_rectangle(ctx
, w_width
/2-width
/2, w_height
/2+height
, int(width
*add_progress
)+.5, bar_height
)
121 def draw_text_pill(left_text
, right_text
, x
=0, y
=0, border
=2, radius
=14, font_desc
=None):
122 if gpodder
.ui
.fremantle
:
124 # Create temporary context to calculate the text size
125 ctx
= cairo
.Context(cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, 1, 1))
127 # Use GTK+ style of a normal Button
129 style
= widget
.rc_get_style()
133 if font_desc
is None:
134 font_desc
= style
.font_desc
135 font_desc
.set_weight(pango
.WEIGHT_BOLD
)
137 pango_context
= widget
.create_pango_context()
138 layout_left
= pango
.Layout(pango_context
)
139 layout_left
.set_font_description(font_desc
)
140 layout_left
.set_text(left_text
)
141 layout_right
= pango
.Layout(pango_context
)
142 layout_right
.set_font_description(font_desc
)
143 layout_right
.set_text(right_text
)
145 width_left
, height_left
= layout_left
.get_pixel_size()
146 width_right
, height_right
= layout_right
.get_pixel_size()
148 text_height
= max(height_left
, height_right
)
150 image_height
= int(y
+text_height
+border
*2)
151 image_width
= int(x
+width_left
+width_right
+x_border
*4)
152 surface
= cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, image_width
, image_height
)
154 ctx
= pangocairo
.CairoContext(cairo
.Context(surface
))
158 if right_text
== '0':
161 left_side_width
= width_left
+ x_border
*2
162 right_side_width
= width_right
+ x_border
*2
164 rect_width
= left_side_width
+ right_side_width
165 rect_height
= text_height
+ border
*2
166 if left_text
is not None:
167 draw_rounded_rectangle(ctx
,x
,y
,rect_width
,rect_height
,radius
, left_side_width
, RRECT_LEFT_SIDE
, right_text
is None)
168 linear
= cairo
.LinearGradient(x
, y
, x
+left_side_width
/2, y
+rect_height
/2)
169 linear
.add_color_stop_rgba(0, .8, .8, .8, .5)
170 linear
.add_color_stop_rgba(.4, .8, .8, .8, .7)
171 linear
.add_color_stop_rgba(.6, .8, .8, .8, .6)
172 linear
.add_color_stop_rgba(.9, .8, .8, .8, .8)
173 linear
.add_color_stop_rgba(1, .8, .8, .8, .9)
174 ctx
.set_source(linear
)
176 xpos
, ypos
, width_left
, height
= x
+1, y
+1, left_side_width
, rect_height
-2
177 if right_text
is None:
179 draw_rounded_rectangle(ctx
, xpos
, ypos
, rect_width
, height
, radius
, width_left
, RRECT_LEFT_SIDE
, right_text
is None)
180 ctx
.set_source_rgba(1., 1., 1., .3)
181 ctx
.set_line_width(1)
183 draw_rounded_rectangle(ctx
,x
,y
,rect_width
,rect_height
,radius
, left_side_width
, RRECT_LEFT_SIDE
, right_text
is None)
184 ctx
.set_source_rgba(.2, .2, .2, .6)
185 ctx
.set_line_width(1)
188 ctx
.move_to(x
+x_border
, y
+1+border
)
189 ctx
.set_source_rgba( 0, 0, 0, 1)
190 ctx
.show_layout(layout_left
)
191 ctx
.move_to(x
-1+x_border
, y
+border
)
192 ctx
.set_source_rgba( 1, 1, 1, 1)
193 ctx
.show_layout(layout_left
)
195 if right_text
is not None:
196 draw_rounded_rectangle(ctx
, x
, y
, rect_width
, rect_height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
197 linear
= cairo
.LinearGradient(x
+left_side_width
, y
, x
+left_side_width
+right_side_width
/2, y
+rect_height
)
198 linear
.add_color_stop_rgba(0, .2, .2, .2, .9)
199 linear
.add_color_stop_rgba(.4, .2, .2, .2, .8)
200 linear
.add_color_stop_rgba(.6, .2, .2, .2, .6)
201 linear
.add_color_stop_rgba(.9, .2, .2, .2, .7)
202 linear
.add_color_stop_rgba(1, .2, .2, .2, .5)
203 ctx
.set_source(linear
)
205 xpos
, ypos
, width
, height
= x
, y
+1, rect_width
-1, rect_height
-2
206 if left_text
is None:
207 xpos
, width
= x
+1, rect_width
-2
208 draw_rounded_rectangle(ctx
, xpos
, ypos
, width
, height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
209 ctx
.set_source_rgba(1., 1., 1., .3)
210 ctx
.set_line_width(1)
212 draw_rounded_rectangle(ctx
, x
, y
, rect_width
, rect_height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
213 ctx
.set_source_rgba(.1, .1, .1, .6)
214 ctx
.set_line_width(1)
217 ctx
.move_to(x
+left_side_width
+x_border
, y
+1+border
)
218 ctx
.set_source_rgba( 0, 0, 0, 1)
219 ctx
.show_layout(layout_right
)
220 ctx
.move_to(x
-1+left_side_width
+x_border
, y
+border
)
221 ctx
.set_source_rgba( 1, 1, 1, 1)
222 ctx
.show_layout(layout_right
)
227 def draw_pill_pixbuf(left_text
, right_text
):
228 return cairo_surface_to_pixbuf(draw_text_pill(left_text
, right_text
))
231 def cairo_surface_to_pixbuf(s
):
233 Converts a Cairo surface to a Gtk Pixbuf by
234 encoding it as PNG and using the PixbufLoader.
236 sio
= StringIO
.StringIO()
240 # Write an empty PNG file to the StringIO, so
241 # in case of an error we have "something" to
242 # load. This happens in PyCairo < 1.1.6, see:
243 # http://webcvs.cairographics.org/pycairo/NEWS?view=markup
244 # Thanks to Chris Arnold for reporting this bug
245 sio
.write('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9cMEQkqIyxn3RkAAAAZdEVYdENv\nbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJ\nRU5ErkJggg==\n'.decode('base64'))
247 pbl
= gtk
.gdk
.PixbufLoader()
248 pbl
.write(sio
.getvalue())
251 pixbuf
= pbl
.get_pixbuf()
254 def progressbar_pixbuf(width
, height
, percentage
):
255 COLOR_BG
= (.4, .4, .4, .4)
256 COLOR_FG
= (.2, .9, .2, 1.)
257 COLOR_FG_HIGH
= (1., 1., 1., .5)
258 COLOR_BORDER
= (0., 0., 0., 1.)
260 surface
= cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, width
, height
)
261 ctx
= cairo
.Context(surface
)
263 padding
= int(float(width
)/8.0)
264 bar_width
= 2*padding
265 bar_height
= height
- 2*padding
266 bar_height_fill
= bar_height
*percentage
269 ctx
.rectangle(padding
, padding
, bar_width
, bar_height
)
270 ctx
.set_source_rgba(*COLOR_BG
)
274 ctx
.rectangle(padding
, padding
+bar_height
-bar_height_fill
, bar_width
, bar_height_fill
)
275 ctx
.set_source_rgba(*COLOR_FG
)
277 ctx
.rectangle(padding
+bar_width
/3, padding
+bar_height
-bar_height_fill
, bar_width
/4, bar_height_fill
)
278 ctx
.set_source_rgba(*COLOR_FG_HIGH
)
282 ctx
.rectangle(padding
-.5, padding
-.5, bar_width
+1, bar_height
+1)
283 ctx
.set_source_rgba(*COLOR_BORDER
)
284 ctx
.set_line_width(1.)
287 return cairo_surface_to_pixbuf(surface
)