1
# -*- coding: utf-8 -*-
3
# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
4
# Authors: Evan Dandrea <evan.dandrea@canonical.com>
6
# Copyright 2009-2010 Canonical Ltd.
8
# This program is free software; you can redistribute it and/or
9
# modify it under the terms of the GNU General Public License
10
# as published by the Free Software Foundation; either version 2
11
# of the License, or (at your option) any later version.
13
# This program is distributed in the hope that it will be useful, but
14
# WITHOUT ANY WARRANTY; without even the implied warranties of
15
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
16
# PURPOSE. See the GNU General Public License for more details.
18
# You should have received a copy of the GNU General Public License along
19
# with this program. If not, see <http://www.gnu.org/licenses/>.
21
"""A set of useful widgets."""
27
DEFAULT_BG = '#393834'
28
DEFAULT_FG = '#FFFFFF'
29
DEFAULT_PADDING = (10, 10)
32
class Loading(gtk.HBox):
33
"""A spinner and a label."""
35
def __init__(self, label, fg_color=None, *args, **kwargs):
36
super(Loading, self).__init__(*args, **kwargs)
37
self.label = gtk.Label(label)
38
self.spinner = gtk.Spinner()
41
if fg_color is not None:
42
self.spinner.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(fg_color))
43
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(fg_color))
45
self.pack_start(self.spinner, expand=False)
46
self.pack_start(self.label, expand=False)
52
class LabelLoading(gtk.HBox):
53
"""A spinner and a label."""
55
def __init__(self, loading_label, fg_color=None, *args, **kwargs):
56
super(LabelLoading, self).__init__(*args, **kwargs)
57
self.loading = Loading(loading_label, fg_color=fg_color)
59
self.label = gtk.Label()
60
if fg_color is not None:
61
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(fg_color))
63
self.pack_start(self.label, expand=False)
64
self.pack_start(self.loading, expand=False)
71
"""Whether the Loading widget is visible or not."""
72
return not self.label.get_visible() and self.loading.get_visible()
75
"""Show the Loading instead of the Label widget."""
80
"""Show the label instead of the Loading widget."""
84
def set_text(self, text):
85
"""Set 'text' to be the label's text."""
86
self.label.set_text(text)
88
def set_markup(self, text):
89
"""Set 'text' to be the label's markup."""
90
self.label.set_markup(text)
93
"""Get the label's text."""
94
return self.label.get_text()
97
"""Get the label's markup."""
98
return self.label.get_label()
101
class PanelTitle(gtk.EventBox):
102
"""A box with a given color and text."""
104
def __init__(self, markup='', *args, **kwargs):
105
super(PanelTitle, self).__init__(*args, **kwargs)
106
self.modify_bg(gtk.STATE_NORMAL, gtk.gdk.Color(DEFAULT_BG))
108
self.label = gtk.Label()
109
self.label.set_markup(markup)
110
self.label.set_padding(*DEFAULT_PADDING)
111
self.label.modify_fg(gtk.STATE_NORMAL, gtk.gdk.Color(DEFAULT_FG))
112
self.label.set_property('xalign', 0.0)
113
self.label.set_line_wrap(True)
114
self.label.set_line_wrap_mode(pango.WRAP_WORD)
115
self.label.connect('size-allocate', self.on_size_allocate)
121
def on_size_allocate(self, widget, allocation):
122
"""The widget can re rezised, embrase it!."""
123
widget.set_size_request(allocation.width - 2, -1)
126
# Modified from John Stowers' client-side-windows demo.
127
class GreyableBin(gtk.Bin):
130
Provides a 'greyed' boolean property that, when set, the bin gets greyed
135
# Invalid name, Missing docstring, do not list fix-mes
136
# pylint: disable=C0103,C0111,W0511
139
"damage_event": "override",
142
'greyed': (gobject.TYPE_BOOLEAN,
143
'Greyed', 'greyed', False, gobject.PARAM_READWRITE),
145
__gtype_name__ = 'GreyableBin'
148
gtk.Bin.__init__(self)
151
self.offscreen_window = None
154
self.unset_flags(gtk.NO_WINDOW)
156
def do_set_property(self, pspec, value):
157
setattr(self, pspec.name, value)
159
def do_get_property(self, pspec):
160
return getattr(self, pspec.name)
162
def _to_child(self, widget_x, widget_y):
163
return widget_x, widget_y
165
def _to_parent(self, offscreen_x, offscreen_y):
166
return offscreen_x, offscreen_y
168
def _pick_offscreen_child(self, offscreen_window, widget_x, widget_y):
169
if self.child and self.child.flags() & gtk.VISIBLE:
170
x, y = self._to_child(widget_x, widget_y)
171
ca = self.child.allocation
172
if (x >= 0 and x < ca.width and y >= 0 and y < ca.height):
173
return self.offscreen_window
176
def _offscreen_window_to_parent(self, offscreen_window, offscreen_x,
177
offscreen_y, parent_x, parent_y):
178
# Unused variable 'y', Unused variable 'x'
179
# pylint: disable=W0612
180
x, y = self._to_parent(offscreen_x, offscreen_y)
181
offscreen_x = parent_x
182
offscreen_y = offscreen_x
184
def _offscreen_window_from_parent(self, parent_window, parent_x, parent_y,
185
offscreen_x, offscreen_y):
186
# Unused variable 'y', Unused variable 'x'
187
# pylint: disable=W0612
188
x, y = self._to_child(parent_x, parent_y)
189
offscreen_x = parent_x
190
offscreen_y = offscreen_x
192
def do_realize(self):
193
self.set_flags(gtk.REALIZED)
195
border_width = self.border_width
197
w = self.allocation.width - 2 * border_width
198
h = self.allocation.height - 2 * border_width
200
self.window = gtk.gdk.Window(
201
self.get_parent_window(),
202
x=self.allocation.x + border_width,
203
y=self.allocation.y + border_width,
206
window_type=gtk.gdk.WINDOW_CHILD,
207
event_mask=self.get_events()
208
| gtk.gdk.EXPOSURE_MASK
209
| gtk.gdk.POINTER_MOTION_MASK
210
| gtk.gdk.BUTTON_PRESS_MASK
211
| gtk.gdk.BUTTON_RELEASE_MASK
212
| gtk.gdk.SCROLL_MASK
213
| gtk.gdk.ENTER_NOTIFY_MASK
214
| gtk.gdk.LEAVE_NOTIFY_MASK,
215
visual=self.get_visual(),
216
colormap=self.get_colormap(),
217
wclass=gtk.gdk.INPUT_OUTPUT)
219
self.window.set_user_data(self)
220
self.window.connect("pick-embedded-child", self._pick_offscreen_child)
222
if self.child and self.child.flags() & gtk.VISIBLE:
223
w = self.child.allocation.width
224
h = self.child.allocation.height
226
self.offscreen_window = gtk.gdk.Window(
227
self.get_root_window(),
228
x=self.allocation.x + border_width,
229
y=self.allocation.y + border_width,
232
window_type=gtk.gdk.WINDOW_OFFSCREEN,
233
event_mask=self.get_events()
234
| gtk.gdk.EXPOSURE_MASK
235
| gtk.gdk.POINTER_MOTION_MASK
236
| gtk.gdk.BUTTON_PRESS_MASK
237
| gtk.gdk.BUTTON_RELEASE_MASK
238
| gtk.gdk.SCROLL_MASK
239
| gtk.gdk.ENTER_NOTIFY_MASK
240
| gtk.gdk.LEAVE_NOTIFY_MASK,
241
visual=self.get_visual(),
242
colormap=self.get_colormap(),
243
wclass=gtk.gdk.INPUT_OUTPUT)
244
self.offscreen_window.set_user_data(self)
247
self.child.set_parent_window(self.offscreen_window)
249
gtk.gdk.offscreen_window_set_embedder(self.offscreen_window,
251
self.offscreen_window.connect("to-embedder",
252
self._offscreen_window_to_parent)
253
self.offscreen_window.connect("from-embedder",
254
self._offscreen_window_from_parent)
256
self.style.attach(self.window)
257
self.style.set_background(self.window, gtk.STATE_NORMAL)
258
self.style.set_background(self.offscreen_window, gtk.STATE_NORMAL)
260
self.offscreen_window.show()
262
def do_child_type(self):
263
#FIXME: This never seems to get called...
266
return gtk.Widget.__gtype__
268
def do_unrealize(self):
269
self.offscreen_window.set_user_data(None)
270
self.offscreen_window = None
272
def do_add(self, widget):
274
widget.set_parent_window(self.offscreen_window)
275
widget.set_parent(self)
278
print "Cannot have more than one child"
280
def do_remove(self, widget):
281
was_visible = widget.flags() & gtk.VISIBLE
282
if self.child == widget:
285
if was_visible and (self.flags() & gtk.VISIBLE):
288
def do_forall(self, internal, callback, data):
290
callback(self.child, data)
292
def do_size_request(self, r):
294
if self.child and (self.child.flags() & gtk.VISIBLE):
295
cw, ch = self.child.size_request()
297
r.width = self.border_width + cw
298
r.height = self.border_width + ch
300
def do_size_allocate(self, allocation):
301
self.allocation = allocation
303
border_width = self.border_width
304
w = self.allocation.width - border_width
305
h = self.allocation.height - border_width
307
if self.flags() & gtk.REALIZED:
308
self.window.move_resize(
309
allocation.x + border_width,
310
allocation.y + border_width,
313
if self.child and self.child.flags() & gtk.VISIBLE:
314
ca = gtk.gdk.Rectangle(x=0, y=0, width=w, height=h)
316
if self.flags() & gtk.REALIZED:
317
self.offscreen_window.move_resize(
318
allocation.x + border_width,
319
allocation.y + border_width,
322
self.child.size_allocate(ca)
324
# FIXME this does not play well with the automatic partitioning page
325
# (expose events to the max, causes lockup)
326
def do_damage_event(self, eventexpose):
327
# invalidate the whole window
328
self.window.invalidate_rect(None, False)
331
def do_expose_event(self, event):
332
if self.flags() & gtk.VISIBLE and self.flags() & gtk.MAPPED:
333
if event.window == self.window:
334
pm = gtk.gdk.offscreen_window_get_pixmap(self.offscreen_window)
337
cr = event.window.cairo_create()
340
cr.rectangle(0, 0, w, h)
343
# paint the offscreen child
344
cr.set_source_pixmap(pm, 0, 0)
349
cr.set_source_rgba(0, 0, 0, 0.5)
350
cr.rectangle(0, 0, *event.window.get_geometry()[2:4])
353
elif event.window == self.offscreen_window:
354
self.style.paint_flat_box(
356
gtk.STATE_NORMAL, gtk.SHADOW_NONE,
357
event.area, self, "blah",
360
self.propagate_expose(self.child, event)
364
gobject.type_register(GreyableBin)