Delete button sensitivity in episode selector (bug 993)
[gpodder.git] / src / gpodder / gtkui / widgets.py
bloba23cdbbfbf6f82e6a876439b80d725f38ccb7f11
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
4 # gPodder - A media aggregator and podcast client
5 # Copyright (c) 2005-2010 Thomas Perl and the gPodder Team
7 # gPodder is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 3 of the License, or
10 # (at your option) any later version.
12 # gPodder is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program. If not, see <http://www.gnu.org/licenses/>.
23 # widgets.py -- Additional widgets for gPodder
24 # Thomas Perl <thp@gpodder.org> 2009-03-31
27 import gtk
28 import gobject
29 import pango
31 from xml.sax import saxutils
33 class SimpleMessageArea(gtk.HBox):
34 """A simple, yellow message area. Inspired by gedit.
36 Original C source code:
37 http://svn.gnome.org/viewvc/gedit/trunk/gedit/gedit-message-area.c
38 """
39 def __init__(self, message, buttons=()):
40 gtk.HBox.__init__(self, spacing=6)
41 self.set_border_width(6)
42 self.__in_style_set = False
43 self.connect('style-set', self.__style_set)
44 self.connect('expose-event', self.__expose_event)
46 self.__label = gtk.Label()
47 self.__label.set_alignment(0.0, 0.5)
48 self.__label.set_line_wrap(False)
49 self.__label.set_ellipsize(pango.ELLIPSIZE_END)
50 self.__label.set_markup('<b>%s</b>' % saxutils.escape(message))
51 self.pack_start(self.__label, expand=True, fill=True)
53 hbox = gtk.HBox()
54 for button in buttons:
55 hbox.pack_start(button, expand=True, fill=False)
56 self.pack_start(hbox, expand=False, fill=False)
58 def set_markup(self, markup, line_wrap=True):
59 self.__label.set_markup(markup)
60 self.__label.set_line_wrap(line_wrap)
62 def __style_set(self, widget, previous_style):
63 if self.__in_style_set:
64 return
66 w = gtk.Window(gtk.WINDOW_POPUP)
67 w.set_name('gtk-tooltip')
68 w.ensure_style()
69 style = w.get_style()
71 self.__in_style_set = True
72 self.set_style(style)
73 self.__in_style_set = False
75 w.destroy()
77 self.queue_draw()
79 def __expose_event(self, widget, event):
80 style = widget.get_style()
81 rect = widget.get_allocation()
82 style.paint_flat_box(widget.window, gtk.STATE_NORMAL,
83 gtk.SHADOW_OUT, None, widget, "tooltip",
84 rect.x, rect.y, rect.width, rect.height)
85 return False
87 class NotificationWindow(gtk.Window):
88 """A quick substitution widget for pynotify notifications."""
89 def __init__(self, message, title=None, important=False, widget=None):
90 gtk.Window.__init__(self, gtk.WINDOW_POPUP)
91 self._finished = False
92 message_area = SimpleMessageArea('')
93 arrow = gtk.image_new_from_stock(gtk.STOCK_GO_UP, \
94 gtk.ICON_SIZE_BUTTON)
95 arrow.set_alignment(.5, 0.)
96 arrow.set_padding(6, 0)
97 message_area.pack_start(arrow, False)
98 message_area.reorder_child(arrow, 0)
99 if title is not None:
100 message_area.set_markup('<b>%s</b>\n<small>%s</small>' % (saxutils.escape(title), saxutils.escape(message)))
101 else:
102 message_area.set_markup(saxutils.escape(message))
103 self.add(message_area)
104 self.set_gravity(gtk.gdk.GRAVITY_NORTH_WEST)
105 self.show_all()
106 if widget is not None:
107 _x, _y, ww, hh, _depth = self.window.get_geometry()
108 parent = widget
109 while not isinstance(parent, gtk.Window):
110 parent = parent.get_parent()
111 x, y, _w, _h, _depth = parent.window.get_geometry()
112 rect = widget.allocation
113 w, h = rect.width, rect.height
114 x += rect.x
115 y += rect.y
116 arrow_rect = arrow.allocation
117 if h < hh or w < ww:
118 self.move(x+w/2-arrow_rect.x-arrow_rect.width/2, y+h-5)
119 else:
120 self.move(x+w/2-ww/2, y+h/2-hh/2+20)
121 message_area.remove(arrow)
123 def show_timeout(self, timeout=8000):
124 gobject.timeout_add(timeout, self._hide_and_destroy)
125 self.show_all()
127 def _hide_and_destroy(self):
128 if not self._finished:
129 self.destroy()
130 self._finished = True
131 return False
133 class SpinningProgressIndicator(gtk.Image):
134 # Progress indicator loading inspired by glchess from gnome-games-clutter
135 def __init__(self, size=32):
136 gtk.Image.__init__(self)
138 self._frames = []
139 self._frame_id = 0
141 # Load the progress indicator
142 icon_theme = gtk.icon_theme_get_default()
144 ICON = lambda x: x
145 try:
146 icon = icon_theme.load_icon(ICON('process-working'), size, 0)
147 width, height = icon.get_width(), icon.get_height()
148 if width < size or height < size:
149 size = min(width, height)
150 for row in range(height/size):
151 for column in range(width/size):
152 frame = icon.subpixbuf(column*size, row*size, size, size)
153 self._frames.append(frame)
154 # Remove the first frame (the "idle" icon)
155 if self._frames:
156 self._frames.pop(0)
157 self.step_animation()
158 except:
159 # FIXME: This is not very beautiful :/
160 self.set_from_stock(gtk.STOCK_EXECUTE, gtk.ICON_SIZE_BUTTON)
162 def step_animation(self):
163 if len(self._frames) > 1:
164 self._frame_id += 1
165 if self._frame_id >= len(self._frames):
166 self._frame_id = 0
167 self.set_from_pixbuf(self._frames[self._frame_id])