~ubuntu-branches/ubuntu/quantal/ubuntuone-control-panel/quantal-proposed

« back to all changes in this revision

Viewing changes to ubuntuone/controlpanel/gtk/widgets.py

  • Committer: Bazaar Package Importer
  • Author(s): Natalia Bidart (nessita)
  • Date: 2010-12-06 12:27:11 UTC
  • Revision ID: james.westby@ubuntu.com-20101206122711-0wvvlliao34bjztf
Tags: upstream-0.0.9
ImportĀ upstreamĀ versionĀ 0.0.9

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# -*- coding: utf-8 -*-
 
2
 
 
3
# Authors: Natalia B. Bidart <nataliabidart@canonical.com>
 
4
# Authors: Evan Dandrea <evan.dandrea@canonical.com>
 
5
#
 
6
# Copyright 2009-2010 Canonical Ltd.
 
7
#
 
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.
 
12
#
 
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.
 
17
#
 
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/>.
 
20
 
 
21
"""A set of useful widgets."""
 
22
 
 
23
import gtk
 
24
import gobject
 
25
import pango
 
26
 
 
27
DEFAULT_BG = '#393834'
 
28
DEFAULT_FG = '#FFFFFF'
 
29
DEFAULT_PADDING = (10, 10)
 
30
 
 
31
 
 
32
class Loading(gtk.HBox):
 
33
    """A spinner and a label."""
 
34
 
 
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()
 
39
        self.spinner.start()
 
40
 
 
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))
 
44
 
 
45
        self.pack_start(self.spinner, expand=False)
 
46
        self.pack_start(self.label, expand=False)
 
47
        self.set_spacing(5)
 
48
 
 
49
        self.show_all()
 
50
 
 
51
 
 
52
class LabelLoading(gtk.HBox):
 
53
    """A spinner and a label."""
 
54
 
 
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)
 
58
 
 
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))
 
62
 
 
63
        self.pack_start(self.label, expand=False)
 
64
        self.pack_start(self.loading, expand=False)
 
65
 
 
66
        self.show()
 
67
        self.start()
 
68
 
 
69
    @property
 
70
    def active(self):
 
71
        """Whether the Loading widget is visible or not."""
 
72
        return not self.label.get_visible() and self.loading.get_visible()
 
73
 
 
74
    def start(self):
 
75
        """Show the Loading instead of the Label widget."""
 
76
        self.label.hide()
 
77
        self.loading.show()
 
78
 
 
79
    def stop(self):
 
80
        """Show the label instead of the Loading widget."""
 
81
        self.label.show()
 
82
        self.loading.hide()
 
83
 
 
84
    def set_text(self, text):
 
85
        """Set 'text' to be the label's text."""
 
86
        self.label.set_text(text)
 
87
 
 
88
    def set_markup(self, text):
 
89
        """Set 'text' to be the label's markup."""
 
90
        self.label.set_markup(text)
 
91
 
 
92
    def get_text(self):
 
93
        """Get the label's text."""
 
94
        return self.label.get_text()
 
95
 
 
96
    def get_label(self):
 
97
        """Get the label's markup."""
 
98
        return self.label.get_label()
 
99
 
 
100
 
 
101
class PanelTitle(gtk.EventBox):
 
102
    """A box with a given color and text."""
 
103
 
 
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))
 
107
 
 
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)
 
116
 
 
117
        self.add(self.label)
 
118
 
 
119
        self.show_all()
 
120
 
 
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)
 
124
 
 
125
 
 
126
# Modified from John Stowers' client-side-windows demo.
 
127
class GreyableBin(gtk.Bin):
 
128
    """A greyable bin.
 
129
 
 
130
    Provides a 'greyed' boolean property that, when set, the bin gets greyed
 
131
    out.
 
132
 
 
133
    """
 
134
 
 
135
    # Invalid name, Missing docstring, do not list fix-mes
 
136
    # pylint: disable=C0103,C0111,W0511
 
137
 
 
138
    __gsignals__ = {
 
139
        "damage_event": "override",
 
140
    }
 
141
    __gproperties__ = {
 
142
        'greyed': (gobject.TYPE_BOOLEAN,
 
143
                   'Greyed', 'greyed', False, gobject.PARAM_READWRITE),
 
144
    }
 
145
    __gtype_name__ = 'GreyableBin'
 
146
 
 
147
    def __init__(self):
 
148
        gtk.Bin.__init__(self)
 
149
 
 
150
        self.child = None
 
151
        self.offscreen_window = None
 
152
        self.greyed = False
 
153
 
 
154
        self.unset_flags(gtk.NO_WINDOW)
 
155
 
 
156
    def do_set_property(self, pspec, value):
 
157
        setattr(self, pspec.name, value)
 
158
 
 
159
    def do_get_property(self, pspec):
 
160
        return getattr(self, pspec.name)
 
161
 
 
162
    def _to_child(self, widget_x, widget_y):
 
163
        return widget_x, widget_y
 
164
 
 
165
    def _to_parent(self, offscreen_x, offscreen_y):
 
166
        return offscreen_x, offscreen_y
 
167
 
 
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
 
174
        return None
 
175
 
 
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
 
183
 
 
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
 
191
 
 
192
    def do_realize(self):
 
193
        self.set_flags(gtk.REALIZED)
 
194
 
 
195
        border_width = self.border_width
 
196
 
 
197
        w = self.allocation.width - 2 * border_width
 
198
        h = self.allocation.height - 2 * border_width
 
199
 
 
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,
 
204
                width=w,
 
205
                height=h,
 
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)
 
218
 
 
219
        self.window.set_user_data(self)
 
220
        self.window.connect("pick-embedded-child", self._pick_offscreen_child)
 
221
 
 
222
        if self.child and self.child.flags() & gtk.VISIBLE:
 
223
            w = self.child.allocation.width
 
224
            h = self.child.allocation.height
 
225
 
 
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,
 
230
                width=w,
 
231
                height=h,
 
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)
 
245
 
 
246
        if self.child:
 
247
            self.child.set_parent_window(self.offscreen_window)
 
248
 
 
249
        gtk.gdk.offscreen_window_set_embedder(self.offscreen_window,
 
250
                                              self.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)
 
255
 
 
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)
 
259
 
 
260
        self.offscreen_window.show()
 
261
 
 
262
    def do_child_type(self):
 
263
        #FIXME: This never seems to get called...
 
264
        if self.child:
 
265
            return None
 
266
        return gtk.Widget.__gtype__
 
267
 
 
268
    def do_unrealize(self):
 
269
        self.offscreen_window.set_user_data(None)
 
270
        self.offscreen_window = None
 
271
 
 
272
    def do_add(self, widget):
 
273
        if not self.child:
 
274
            widget.set_parent_window(self.offscreen_window)
 
275
            widget.set_parent(self)
 
276
            self.child = widget
 
277
        else:
 
278
            print "Cannot have more than one child"
 
279
 
 
280
    def do_remove(self, widget):
 
281
        was_visible = widget.flags() & gtk.VISIBLE
 
282
        if self.child == widget:
 
283
            widget.unparent()
 
284
            self.child = None
 
285
            if was_visible and (self.flags() & gtk.VISIBLE):
 
286
                self.queue_resize()
 
287
 
 
288
    def do_forall(self, internal, callback, data):
 
289
        if self.child:
 
290
            callback(self.child, data)
 
291
 
 
292
    def do_size_request(self, r):
 
293
        cw, ch = 0, 0
 
294
        if self.child and (self.child.flags() & gtk.VISIBLE):
 
295
            cw, ch = self.child.size_request()
 
296
 
 
297
        r.width = self.border_width + cw
 
298
        r.height = self.border_width + ch
 
299
 
 
300
    def do_size_allocate(self, allocation):
 
301
        self.allocation = allocation
 
302
 
 
303
        border_width = self.border_width
 
304
        w = self.allocation.width - border_width
 
305
        h = self.allocation.height - border_width
 
306
 
 
307
        if self.flags() & gtk.REALIZED:
 
308
            self.window.move_resize(
 
309
                            allocation.x + border_width,
 
310
                            allocation.y + border_width,
 
311
                            w, h)
 
312
 
 
313
        if self.child and self.child.flags() & gtk.VISIBLE:
 
314
            ca = gtk.gdk.Rectangle(x=0, y=0, width=w, height=h)
 
315
 
 
316
            if self.flags() & gtk.REALIZED:
 
317
                self.offscreen_window.move_resize(
 
318
                            allocation.x + border_width,
 
319
                            allocation.y + border_width,
 
320
                            w, h)
 
321
 
 
322
            self.child.size_allocate(ca)
 
323
 
 
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)
 
329
        return True
 
330
 
 
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)
 
335
                w, h = pm.get_size()
 
336
 
 
337
                cr = event.window.cairo_create()
 
338
                if self.greyed:
 
339
                    cr.save()
 
340
                cr.rectangle(0, 0, w, h)
 
341
                cr.clip()
 
342
 
 
343
                # paint the offscreen child
 
344
                cr.set_source_pixmap(pm, 0, 0)
 
345
                cr.paint()
 
346
 
 
347
                if self.greyed:
 
348
                    cr.restore()
 
349
                    cr.set_source_rgba(0, 0, 0, 0.5)
 
350
                    cr.rectangle(0, 0, *event.window.get_geometry()[2:4])
 
351
                    cr.paint()
 
352
 
 
353
            elif event.window == self.offscreen_window:
 
354
                self.style.paint_flat_box(
 
355
                                event.window,
 
356
                                gtk.STATE_NORMAL, gtk.SHADOW_NONE,
 
357
                                event.area, self, "blah",
 
358
                                0, 0, -1, -1)
 
359
                if self.child:
 
360
                    self.propagate_expose(self.child, event)
 
361
 
 
362
        return False
 
363
 
 
364
gobject.type_register(GreyableBin)