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 assert left_side_width
is not None
50 if close
: offset
= 0.5
52 if sides_to_draw
& RRECT_LEFT_SIDE
:
53 ctx
.move_to(x
+int(left_side_width
)-offset
, y
+h
)
55 ctx
.curve_to(x
, y
+h
, x
, y
+h
, x
, y
+h
-r
)
57 ctx
.curve_to(x
, y
, x
, y
, x
+r
, y
)
58 ctx
.line_to(x
+int(left_side_width
)-offset
, y
)
60 ctx
.line_to(x
+int(left_side_width
)-offset
, y
+h
)
62 if sides_to_draw
& RRECT_RIGHT_SIDE
:
63 ctx
.move_to(x
+int(left_side_width
)+offset
, y
)
65 ctx
.curve_to(x
+w
, y
, x
+w
, y
, x
+w
, y
+r
)
66 ctx
.line_to(x
+w
, y
+h
-r
)
67 ctx
.curve_to(x
+w
, y
+h
, x
+w
, y
+h
, x
+w
-r
, y
+h
)
68 ctx
.line_to(x
+int(left_side_width
)+offset
, y
+h
)
70 ctx
.line_to(x
+int(left_side_width
)+offset
, y
)
73 def rounded_rectangle(ctx
, x
, y
, width
, height
, radius
=4.):
74 """Simple rounded rectangle algorithmn
76 http://www.cairographics.org/samples/rounded_rectangle/
78 degrees
= math
.pi
/ 180.
81 ctx
.arc(x
+ width
- radius
, y
+ radius
, radius
, -90. * degrees
, 0)
82 ctx
.arc(x
+ width
- radius
, y
+ height
- radius
, radius
, 0, 90. * degrees
)
83 ctx
.arc(x
+ radius
, y
+ height
- radius
, radius
, 90. * degrees
, 180. * degrees
)
84 ctx
.arc(x
+ radius
, y
+ radius
, radius
, 180. * degrees
, 270. * degrees
)
88 def draw_text_box_centered(ctx
, widget
, w_width
, w_height
, text
, font_desc
=None, add_progress
=None):
89 style
= widget
.rc_get_style()
90 text_color
= style
.text
[gtk
.STATE_PRELIGHT
]
91 red
, green
, blue
= text_color
.red
, text_color
.green
, text_color
.blue
92 text_color
= [float(x
)/65535. for x
in (red
, green
, blue
)]
96 font_desc
= style
.font_desc
97 font_desc
.set_size(14*pango
.SCALE
)
99 pango_context
= widget
.create_pango_context()
100 layout
= pango
.Layout(pango_context
)
101 layout
.set_font_description(font_desc
)
102 layout
.set_text(text
)
103 width
, height
= layout
.get_pixel_size()
105 ctx
.move_to(w_width
/2-width
/2, w_height
/2-height
/2)
106 ctx
.set_source_rgba(*text_color
)
107 ctx
.show_layout(layout
)
109 # Draw an optional progress bar below the text (same width)
110 if add_progress
is not None:
112 ctx
.set_source_rgba(*text_color
)
113 ctx
.set_line_width(1.)
114 rounded_rectangle(ctx
, w_width
/2-width
/2-.5, w_height
/2+height
-.5, width
+1, bar_height
+1)
116 rounded_rectangle(ctx
, w_width
/2-width
/2, w_height
/2+height
, int(width
*add_progress
)+.5, bar_height
)
120 def draw_text_pill(left_text
, right_text
, x
=0, y
=0, border
=2, radius
=14, font_desc
=None):
121 if gpodder
.ui
.fremantle
:
123 # Create temporary context to calculate the text size
124 ctx
= cairo
.Context(cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, 1, 1))
126 # Use GTK+ style of a normal Button
128 style
= widget
.rc_get_style()
132 if font_desc
is None:
133 font_desc
= style
.font_desc
134 font_desc
.set_weight(pango
.WEIGHT_BOLD
)
136 pango_context
= widget
.create_pango_context()
137 layout_left
= pango
.Layout(pango_context
)
138 layout_left
.set_font_description(font_desc
)
139 layout_left
.set_text(left_text
)
140 layout_right
= pango
.Layout(pango_context
)
141 layout_right
.set_font_description(font_desc
)
142 layout_right
.set_text(right_text
)
144 width_left
, height_left
= layout_left
.get_pixel_size()
145 width_right
, height_right
= layout_right
.get_pixel_size()
147 text_height
= max(height_left
, height_right
)
149 image_height
= int(y
+text_height
+border
*2)
150 image_width
= int(x
+width_left
+width_right
+x_border
*4)
151 surface
= cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, image_width
, image_height
)
153 ctx
= pangocairo
.CairoContext(cairo
.Context(surface
))
157 if right_text
== '0':
160 left_side_width
= width_left
+ x_border
*2
161 right_side_width
= width_right
+ x_border
*2
163 rect_width
= left_side_width
+ right_side_width
164 rect_height
= text_height
+ border
*2
165 if left_text
is not None:
166 draw_rounded_rectangle(ctx
,x
,y
,rect_width
,rect_height
,radius
, left_side_width
, RRECT_LEFT_SIDE
, right_text
is None)
167 linear
= cairo
.LinearGradient(x
, y
, x
+left_side_width
/2, y
+rect_height
/2)
168 linear
.add_color_stop_rgba(0, .8, .8, .8, .5)
169 linear
.add_color_stop_rgba(.4, .8, .8, .8, .7)
170 linear
.add_color_stop_rgba(.6, .8, .8, .8, .6)
171 linear
.add_color_stop_rgba(.9, .8, .8, .8, .8)
172 linear
.add_color_stop_rgba(1, .8, .8, .8, .9)
173 ctx
.set_source(linear
)
175 xpos
, ypos
, width_left
, height
= x
+1, y
+1, left_side_width
, rect_height
-2
176 if right_text
is None:
178 draw_rounded_rectangle(ctx
, xpos
, ypos
, rect_width
, height
, radius
, width_left
, RRECT_LEFT_SIDE
, right_text
is None)
179 ctx
.set_source_rgba(1., 1., 1., .3)
180 ctx
.set_line_width(1)
182 draw_rounded_rectangle(ctx
,x
,y
,rect_width
,rect_height
,radius
, left_side_width
, RRECT_LEFT_SIDE
, right_text
is None)
183 ctx
.set_source_rgba(.2, .2, .2, .6)
184 ctx
.set_line_width(1)
187 ctx
.move_to(x
+x_border
, y
+1+border
)
188 ctx
.set_source_rgba( 0, 0, 0, 1)
189 ctx
.show_layout(layout_left
)
190 ctx
.move_to(x
-1+x_border
, y
+border
)
191 ctx
.set_source_rgba( 1, 1, 1, 1)
192 ctx
.show_layout(layout_left
)
194 if right_text
is not None:
195 draw_rounded_rectangle(ctx
, x
, y
, rect_width
, rect_height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
196 linear
= cairo
.LinearGradient(x
+left_side_width
, y
, x
+left_side_width
+right_side_width
/2, y
+rect_height
)
197 linear
.add_color_stop_rgba(0, .2, .2, .2, .9)
198 linear
.add_color_stop_rgba(.4, .2, .2, .2, .8)
199 linear
.add_color_stop_rgba(.6, .2, .2, .2, .6)
200 linear
.add_color_stop_rgba(.9, .2, .2, .2, .7)
201 linear
.add_color_stop_rgba(1, .2, .2, .2, .5)
202 ctx
.set_source(linear
)
204 xpos
, ypos
, width
, height
= x
, y
+1, rect_width
-1, rect_height
-2
205 if left_text
is None:
206 xpos
, width
= x
+1, rect_width
-2
207 draw_rounded_rectangle(ctx
, xpos
, ypos
, width
, height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
208 ctx
.set_source_rgba(1., 1., 1., .3)
209 ctx
.set_line_width(1)
211 draw_rounded_rectangle(ctx
, x
, y
, rect_width
, rect_height
, radius
, left_side_width
, RRECT_RIGHT_SIDE
, left_text
is None)
212 ctx
.set_source_rgba(.1, .1, .1, .6)
213 ctx
.set_line_width(1)
216 ctx
.move_to(x
+left_side_width
+x_border
, y
+1+border
)
217 ctx
.set_source_rgba( 0, 0, 0, 1)
218 ctx
.show_layout(layout_right
)
219 ctx
.move_to(x
-1+left_side_width
+x_border
, y
+border
)
220 ctx
.set_source_rgba( 1, 1, 1, 1)
221 ctx
.show_layout(layout_right
)
226 def draw_pill_pixbuf(left_text
, right_text
):
227 return cairo_surface_to_pixbuf(draw_text_pill(left_text
, right_text
))
230 def cairo_surface_to_pixbuf(s
):
232 Converts a Cairo surface to a Gtk Pixbuf by
233 encoding it as PNG and using the PixbufLoader.
235 sio
= StringIO
.StringIO()
239 # Write an empty PNG file to the StringIO, so
240 # in case of an error we have "something" to
241 # load. This happens in PyCairo < 1.1.6, see:
242 # http://webcvs.cairographics.org/pycairo/NEWS?view=markup
243 # Thanks to Chris Arnold for reporting this bug
244 sio
.write('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A\n/wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9cMEQkqIyxn3RkAAAAZdEVYdENv\nbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJ\nRU5ErkJggg==\n'.decode('base64'))
246 pbl
= gtk
.gdk
.PixbufLoader()
247 pbl
.write(sio
.getvalue())
250 pixbuf
= pbl
.get_pixbuf()
253 def progressbar_pixbuf(width
, height
, percentage
):
254 COLOR_BG
= (.4, .4, .4, .4)
255 COLOR_FG
= (.2, .9, .2, 1.)
256 COLOR_FG_HIGH
= (1., 1., 1., .5)
257 COLOR_BORDER
= (0., 0., 0., 1.)
259 surface
= cairo
.ImageSurface(cairo
.FORMAT_ARGB32
, width
, height
)
260 ctx
= cairo
.Context(surface
)
262 padding
= int(float(width
)/8.0)
263 bar_width
= 2*padding
264 bar_height
= height
- 2*padding
265 bar_height_fill
= bar_height
*percentage
268 ctx
.rectangle(padding
, padding
, bar_width
, bar_height
)
269 ctx
.set_source_rgba(*COLOR_BG
)
273 ctx
.rectangle(padding
, padding
+bar_height
-bar_height_fill
, bar_width
, bar_height_fill
)
274 ctx
.set_source_rgba(*COLOR_FG
)
276 ctx
.rectangle(padding
+bar_width
/3, padding
+bar_height
-bar_height_fill
, bar_width
/4, bar_height_fill
)
277 ctx
.set_source_rgba(*COLOR_FG_HIGH
)
281 ctx
.rectangle(padding
-.5, padding
-.5, bar_width
+1, bar_height
+1)
282 ctx
.set_source_rgba(*COLOR_BORDER
)
283 ctx
.set_line_width(1.)
286 return cairo_surface_to_pixbuf(surface
)