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
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
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)
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, min_width
=3, max_width
=100):
59 # The longest line should determine the size of the label
60 width_chars
= max(len(line
) for line
in markup
.splitlines())
62 # Enforce upper and lower limits for the width
63 width_chars
= max(min_width
, min(max_width
, width_chars
))
65 self
.__label
.set_width_chars(width_chars
)
66 self
.__label
.set_markup(markup
)
67 self
.__label
.set_line_wrap(line_wrap
)
69 def __style_set(self
, widget
, previous_style
):
70 if self
.__in
_style
_set
:
73 w
= gtk
.Window(gtk
.WINDOW_POPUP
)
74 w
.set_name('gtk-tooltip')
78 self
.__in
_style
_set
= True
80 self
.__label
.set_style(style
)
81 self
.__in
_style
_set
= False
87 def __expose_event(self
, widget
, event
):
88 style
= widget
.get_style()
89 rect
= widget
.get_allocation()
90 style
.paint_flat_box(widget
.window
, gtk
.STATE_NORMAL
,
91 gtk
.SHADOW_OUT
, None, widget
, "tooltip",
92 rect
.x
, rect
.y
, rect
.width
, rect
.height
)
95 class NotificationWindow(gtk
.Window
):
96 """A quick substitution widget for pynotify notifications."""
97 def __init__(self
, message
, title
=None, important
=False, widget
=None):
98 gtk
.Window
.__init
__(self
, gtk
.WINDOW_POPUP
)
99 self
._finished
= False
100 message_area
= SimpleMessageArea('')
101 arrow
= gtk
.image_new_from_stock(gtk
.STOCK_GO_UP
, \
102 gtk
.ICON_SIZE_BUTTON
)
103 arrow
.set_alignment(.5, 0.)
104 arrow
.set_padding(6, 0)
105 message_area
.pack_start(arrow
, False)
106 message_area
.reorder_child(arrow
, 0)
107 if title
is not None:
108 message_area
.set_markup('<b>%s</b>\n<small>%s</small>' % (saxutils
.escape(title
), saxutils
.escape(message
)))
110 message_area
.set_markup(saxutils
.escape(message
))
111 self
.add(message_area
)
112 self
.set_gravity(gtk
.gdk
.GRAVITY_NORTH_WEST
)
114 if widget
is not None:
115 _x
, _y
, ww
, hh
, _depth
= self
.window
.get_geometry()
117 while not isinstance(parent
, gtk
.Window
):
118 parent
= parent
.get_parent()
119 x
, y
, _w
, _h
, _depth
= parent
.window
.get_geometry()
120 rect
= widget
.allocation
121 w
, h
= rect
.width
, rect
.height
124 arrow_rect
= arrow
.allocation
126 self
.move(x
+w
/2-arrow_rect
.x
-arrow_rect
.width
/2, y
+h
-5)
128 self
.move(x
+w
/2-ww
/2, y
+h
/2-hh
/2+20)
129 message_area
.remove(arrow
)
131 def show_timeout(self
, timeout
=8000):
132 gobject
.timeout_add(timeout
, self
._hide
_and
_destroy
)
135 def _hide_and_destroy(self
):
136 if not self
._finished
:
138 self
._finished
= True
141 class SpinningProgressIndicator(gtk
.Image
):
142 # Progress indicator loading inspired by glchess from gnome-games-clutter
143 def __init__(self
, size
=32):
144 gtk
.Image
.__init
__(self
)
149 # Load the progress indicator
150 icon_theme
= gtk
.icon_theme_get_default()
154 icon
= icon_theme
.load_icon(ICON('process-working'), size
, 0)
155 width
, height
= icon
.get_width(), icon
.get_height()
156 if width
< size
or height
< size
:
157 size
= min(width
, height
)
158 for row
in range(height
/size
):
159 for column
in range(width
/size
):
160 frame
= icon
.subpixbuf(column
*size
, row
*size
, size
, size
)
161 self
._frames
.append(frame
)
162 # Remove the first frame (the "idle" icon)
165 self
.step_animation()
167 # FIXME: This is not very beautiful :/
168 self
.set_from_stock(gtk
.STOCK_EXECUTE
, gtk
.ICON_SIZE_BUTTON
)
170 def step_animation(self
):
171 if len(self
._frames
) > 1:
173 if self
._frame
_id
>= len(self
._frames
):
175 self
.set_from_pixbuf(self
._frames
[self
._frame
_id
])