1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
29
"""``miro.frontends.widgets.widgetutil`` -- Utility functions.
35
from miro.frontends.widgets import imagepool
36
from miro.plat import resources
37
from miro.plat.frontends.widgets import widgetset
44
def round_rect(context, x, y, width, height, edge_radius):
45
"""Specifies path of a rectangle with rounded corners.
47
edge_radius = min(edge_radius, min(width, height)/2.0)
48
inner_width = width - edge_radius*2
49
inner_height = height - edge_radius*2
50
x_inner1 = x + edge_radius
51
x_inner2 = x + width - edge_radius
52
y_inner1 = y + edge_radius
53
y_inner2 = y + height - edge_radius
55
context.move_to(x+edge_radius, y)
56
context.rel_line_to(inner_width, 0)
57
context.arc(x_inner2, y_inner1, edge_radius, -PI/2, 0)
58
context.rel_line_to(0, inner_height)
59
context.arc(x_inner2, y_inner2, edge_radius, 0, PI/2)
60
context.rel_line_to(-inner_width, 0)
61
context.arc(x_inner1, y_inner2, edge_radius, PI/2, PI)
62
context.rel_line_to(0, -inner_height)
63
context.arc(x_inner1, y_inner1, edge_radius, PI, PI*3/2)
65
def round_rect_reverse(context, x, y, width, height, edge_radius):
66
"""Specifies path of a rectangle with rounded corners.
68
This specifies the rectangle in a counter-clockwise fashion.
70
edge_radius = min(edge_radius, min(width, height)/2.0)
71
inner_width = width - edge_radius*2
72
inner_height = height - edge_radius*2
73
x_inner1 = x + edge_radius
74
x_inner2 = x + width - edge_radius
75
y_inner1 = y + edge_radius
76
y_inner2 = y + height - edge_radius
78
context.move_to(x+edge_radius, y)
79
context.arc_negative(x_inner1, y_inner1, edge_radius, PI*3/2, PI)
80
context.rel_line_to(0, inner_height)
81
context.arc_negative(x_inner1, y_inner2, edge_radius, PI, PI/2)
82
context.rel_line_to(inner_width, 0)
83
context.arc_negative(x_inner2, y_inner2, edge_radius, PI/2, 0)
84
context.rel_line_to(0, -inner_height)
85
context.arc_negative(x_inner2, y_inner1, edge_radius, 0, -PI/2)
86
context.rel_line_to(-inner_width, 0)
88
def circular_rect(context, x, y, width, height):
89
"""Make a path for a rectangle with the left/right side being circles.
92
inner_width = width - height
95
inner_x2 = inner_x1 + inner_width
97
context.move_to(inner_x1, y)
98
context.rel_line_to(inner_width, 0)
99
context.arc(inner_x2, inner_y, radius, -PI/2, PI/2)
100
context.rel_line_to(-inner_width, 0)
101
context.arc(inner_x1, inner_y, radius, PI/2, -PI/2)
103
def circular_rect_negative(context, x, y, width, height):
104
"""The same path as ``circular_rect()``, but going counter clockwise.
106
radius = height / 2.0
107
inner_width = width - height
109
inner_x1 = x + radius
110
inner_x2 = inner_x1 + inner_width
112
context.move_to(inner_x1, y)
113
context.arc_negative(inner_x1, inner_y, radius, -PI/2, PI/2)
114
context.rel_line_to(inner_width, 0)
115
context.arc_negative(inner_x2, inner_y, radius, PI/2, -PI/2)
116
context.rel_line_to(-inner_width, 0)
118
def draw_rounded_icon(context, icon, x, y, width, height, inset=0, fraction=1.0):
119
"""Draw an icon with the corners rounded.
121
x, y, width, height define where the box is.
123
inset creates a margin between where the images is drawn and (x, y, width,
127
round_rect(context, x + inset, y + inset, width - inset*2,
130
if icon.width != width or icon.height != height:
131
icon_x = int((width - icon.width) / 2)
132
icon_y = int((height - icon.height) / 2)
133
context.set_color((0, 0, 0), fraction)
136
context.rectangle(x, y, icon_x, height)
138
context.rectangle(x+icon_x+icon.width, y, width-(icon_x+icon.width), height)
141
context.rectangle(x, y, width, icon_y)
143
context.rectangle(x, y+icon_y+icon.height, width, height-(icon_y+icon.height))
146
context.rectangle(x, y, width, height)
150
icon.draw(context, x + icon_x, y + icon_y, icon.width, icon.height, fraction)
153
def align(widget, xalign=0, yalign=0, xscale=0, yscale=0,
154
top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
155
"""Create an alignment, then add widget to it and return the alignment.
157
alignment = widgetset.Alignment(xalign, yalign, xscale, yscale)
158
alignment.set_padding(top_pad, bottom_pad, left_pad, right_pad)
159
alignment.add(widget)
162
def align_center(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
163
"""Wrap a widget in an Alignment that will center it horizontally.
165
return align(widget, 0.5, 0, 0, 1,
166
top_pad, bottom_pad, left_pad, right_pad)
168
def align_right(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
169
"""Wrap a widget in an Alignment that will align it left.
171
return align(widget, 1, 0, 0, 1, top_pad, bottom_pad, left_pad, right_pad)
173
def align_left(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
174
"""Wrap a widget in an Alignment that will align it right.
176
return align(widget, 0, 0, 0, 1, top_pad, bottom_pad, left_pad, right_pad)
178
def align_middle(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
179
"""Wrap a widget in an Alignment that will center it vertically.
181
return align(widget, 0, 0.5, 1, 0,
182
top_pad, bottom_pad, left_pad, right_pad)
184
def align_top(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
185
"""Wrap a widget in an Alignment that will align to the top.
187
return align(widget, 0, 0, 1, 0, top_pad, bottom_pad, left_pad, right_pad)
189
def align_bottom(widget, top_pad=0, bottom_pad=0, left_pad=0, right_pad=0):
190
"""Wrap a widget in an Alignment that will align to the bottom.
192
return align(widget, 0, 1, 1, 0, top_pad, bottom_pad, left_pad, right_pad)
194
def pad(widget, top=0, bottom=0, left=0, right=0):
195
"""Wrap a widget in an Alignment that will pad it.
197
alignment = widgetset.Alignment(xscale=1, yscale=1)
198
alignment.set_padding(top, bottom, left, right)
199
alignment.add(widget)
202
def build_hbox(items, padding=5):
203
"""Builds an HBox and packs with the list of widgets. Padding defaults to
207
map(lambda item: h.pack_start(item, padding=padding), items)
210
def build_control_line(items, padding=5):
213
max_baseline = max(max_baseline, item.baseline())
216
if item.baseline() == max_baseline:
217
padded_items.append(item)
219
pad = int(round(max_baseline - item.baseline()))
220
padded_items.append(align_bottom(item, bottom_pad=pad))
221
return build_hbox(padded_items, padding)
223
def make_surface(image_name):
224
path = resources.path("images/%s.png" % image_name)
225
return imagepool.get_surface(path)
227
class ThreeImageSurface(object):
228
"""Takes a left, center and right image and draws them to an arbitrary
229
width. If the width is greater than the combined width of the 3 images,
230
then the center image will be tiled to compensate.
234
>>> timelinebar = ThreeImageSurface("timelinebar")
236
This creates a ``ThreeImageSurface`` using the images
237
``images/timelinebar_left.png``, ``images/timelinebar_center.png``, and
238
``images/timelinebar_right.png``.
242
>>> timelinebar = ThreeImageSurface()
243
>>> img_left = make_surface("timelinebar_left")
244
>>> img_center = make_surface("timelinebar_center")
245
>>> img_right = make_surface("timelinebar_right")
246
>>> timelinebar.set_images(img_left, img_center, img_right)
248
This does the same thing, but allows you to explicitly set which images
251
def __init__(self, basename=None):
252
self.left = self.center = self.right = None
254
if basename is not None:
255
left = make_surface(basename + '_left')
256
center = make_surface(basename + '_center')
257
right = make_surface(basename + '_right')
258
self.set_images(left, center, right)
260
def set_images(self, left, center, right):
261
"""Sets the left, center and right images to use.
266
if not (self.left.height == self.center.height == self.right.height):
267
raise ValueError("Images aren't the same height")
268
self.height = self.left.height
270
def draw(self, context, x, y, width, fraction=1.0):
271
left_width = min(self.left.width, width)
272
self.left.draw(context, x, y, left_width, self.height, fraction)
273
self.draw_right(context, x + left_width, y, width - left_width, fraction)
275
def draw_right(self, context, x, y, width, fraction=1.0):
276
# draws only the right two images
278
right_width = min(self.right.width, width)
279
center_width = int(width - right_width)
281
self.center.draw(context, x, y, center_width, self.height, fraction)
282
self.right.draw(context, x + center_width, y, right_width, self.height, fraction)
284
class HideableWidget(widgetset.VBox):
285
"""Creates a widget that can be hidden and shown.
289
>>> lab = Label(_("Error!"))
290
>>> hidden_lab = HideableWidget(lab)
292
Then when we want to hide it, we do:
294
>>> hidden_lab.hide()
296
and when we want to show it again, we do:
298
>>> hidden_lab.show()
300
def __init__(self, child):
301
widgetset.VBox.__init__(self)
306
"""Returns the child widget.
311
"""Shows the child widget.
314
self.pack_start(self._child)
318
"""Hides the child widget.
321
self.remove(self._child)
324
class Shadow(object):
325
"""Encapsulates all parameters required to draw shadows.
327
def __init__(self, color, opacity, offset, blur_radius):
329
self.opacity = opacity
331
self.blur_radius = blur_radius
333
def get_feed_info(feed_id):
334
"""Returns the :class:`FeedInfo` object for a given ``feed_id``
335
regardless of whether it's an audio or video feed.
337
tablist = app.tab_list_manager.which_tablist_has_id(feed_id)
338
return tablist.get_info(feed_id)
340
def feed_exists(feed_id):
341
"""Returns true or false as to whether a :class:`Feed` with id ``feed_id``
345
get_feed_info(feed_id)
347
except (ValueError, KeyError):