~juliank/software-center/debian

« back to all changes in this revision

Viewing changes to softwarecenter/ui/gtk3/widgets/containers.py

  • Committer: Julian Andres Klode
  • Date: 2011-11-20 13:34:41 UTC
  • mfrom: (429.62.1824 software-center)
  • Revision ID: jak@debian.org-20111120133441-npw6j3nmd8v75yav
Merge from 2.0.7 up to 5.1.2 pre-release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
import cairo
 
2
import os
 
3
from math import pi as PI
 
4
PI_OVER_180 = PI/180
 
5
import softwarecenter.paths
 
6
 
 
7
from gi.repository import Gtk, Gdk
 
8
 
 
9
from buttons import MoreLink
 
10
from softwarecenter.ui.gtk3.em import StockEms
 
11
from softwarecenter.ui.gtk3.drawing import rounded_rect
 
12
 
 
13
 
 
14
class FlowableGrid(Gtk.Fixed):
 
15
 
 
16
    MIN_HEIGHT = 100
 
17
 
 
18
    def __init__(self, paint_grid_pattern=True):
 
19
        Gtk.Fixed.__init__(self)
 
20
        self.set_size_request(100, -1)
 
21
        self.row_spacing = 0
 
22
        self.column_spacing = 0
 
23
        self.n_columns = 0
 
24
        self.n_rows = 0
 
25
        self.paint_grid_pattern = paint_grid_pattern
 
26
        self._cell_size = None
 
27
        return
 
28
 
 
29
    # private
 
30
    def _get_n_columns_for_width(self, width, cell_w, col_spacing):
 
31
        n_cols = width / (cell_w + col_spacing)
 
32
        return n_cols
 
33
 
 
34
    def _layout_children(self, a):
 
35
        if not self.get_visible(): return
 
36
 
 
37
        children = self.get_children()
 
38
        width = a.width
 
39
        #height = a.height
 
40
 
 
41
        col_spacing = 0
 
42
        row_spacing = 0
 
43
 
 
44
        cell_w, cell_h = self.get_cell_size()
 
45
        n_cols = self._get_n_columns_for_width(width, cell_w, col_spacing)
 
46
 
 
47
        if n_cols == 0: return
 
48
        cell_w = width / n_cols
 
49
        self.n_columns = n_cols
 
50
 
 
51
        #~ h_overhang = width - n_cols*cell_w - (n_cols-1)*col_spacing
 
52
        #~ if n_cols > 1:
 
53
            #~ xo = h_overhang / (n_cols-1)
 
54
        #~ else:
 
55
            #~ xo = h_overhang
 
56
        
 
57
        if len(children) % n_cols:
 
58
            self.n_rows = len(children)/n_cols + 1
 
59
        else:
 
60
            self.n_rows = len(children)/n_cols
 
61
 
 
62
        y = 0
 
63
        for i, child in enumerate(children):
 
64
            x = a.x + (i % n_cols) * (cell_w + col_spacing)
 
65
 
 
66
            #~ x = a.x + (i % n_cols) * (cell_w + col_spacing + xo)
 
67
            #~ if n_cols == 1:
 
68
                #~ x += xo/2
 
69
            if (i%n_cols) == 0:
 
70
                y = a.y + (i / n_cols) * (cell_h + row_spacing)
 
71
 
 
72
            child_alloc = child.get_allocation()
 
73
            child_alloc.x = x
 
74
            child_alloc.y = y
 
75
            child_alloc.width = cell_w
 
76
            child_alloc.height = cell_h
 
77
            child.size_allocate(child_alloc)
 
78
        return
 
79
 
 
80
    # overrides
 
81
    def do_get_request_mode(self):
 
82
        return Gtk.SizeRequestMode.HEIGHT_FOR_WIDTH
 
83
 
 
84
    def do_get_preferred_height_for_width(self, width):
 
85
        old = self.get_allocation()
 
86
        if width == old.width: old.height, old.height
 
87
 
 
88
        cell_w, cell_h = self.get_cell_size()
 
89
        n_cols = self._get_n_columns_for_width(
 
90
                        width, cell_w, self.column_spacing)
 
91
 
 
92
        if not n_cols: return self.MIN_HEIGHT, self.MIN_HEIGHT
 
93
 
 
94
        children = self.get_children()
 
95
        n_rows = len(children) / n_cols
 
96
 
 
97
        # store these for use when _layout_children gets called
 
98
        if len(children) % n_cols:
 
99
            n_rows += 1
 
100
 
 
101
        pref_h = n_rows*cell_h + (n_rows-1)*self.row_spacing + 1
 
102
        pref_h = max(self.MIN_HEIGHT, pref_h)
 
103
        return pref_h, pref_h
 
104
 
 
105
    # signal handlers
 
106
    def do_size_allocate(self, allocation):
 
107
        self.set_allocation(allocation)
 
108
        self._layout_children(allocation)
 
109
        return
 
110
 
 
111
    def do_draw(self, cr):
 
112
        if not (self.n_columns and self.n_rows): return
 
113
 
 
114
        if self.paint_grid_pattern:
 
115
            self.render_grid(cr)
 
116
 
 
117
        for child in self: self.propagate_draw(child, cr)
 
118
        return
 
119
 
 
120
    # public
 
121
    def render_grid(self, cr):
 
122
        context = self.get_style_context()
 
123
        context.save()
 
124
        context.add_class("grid-lines")
 
125
        bg = context.get_border_color(self.get_state_flags())
 
126
        context.restore()
 
127
 
 
128
        cr.save()
 
129
 
 
130
        Gdk.cairo_set_source_rgba(cr, bg)
 
131
        cr.set_line_width(1)
 
132
 
 
133
        a = self.get_allocation()
 
134
        w = a.width / self.n_columns
 
135
 
 
136
        for i in range(self.n_columns):
 
137
            cr.move_to(i*w+0.5, 0)
 
138
            cr.rel_line_to(0, a.height-3)
 
139
            cr.stroke()
 
140
 
 
141
        w = a.height / self.n_rows
 
142
 
 
143
        for i in range(self.n_rows):
 
144
            cr.move_to(2, i*w+0.5)
 
145
            cr.rel_line_to(a.width-4, 0)
 
146
            cr.stroke()
 
147
 
 
148
        cr.restore()
 
149
        return
 
150
 
 
151
    def add_child(self, child):
 
152
        self._cell_size = None
 
153
        self.put(child, 0, 0)
 
154
        return
 
155
 
 
156
    def get_cell_size(self):
 
157
        if self._cell_size is not None:
 
158
            return self._cell_size
 
159
 
 
160
        w = h = 1
 
161
        for child in self.get_children():
 
162
            child_pref_w = child.get_preferred_width()[0]
 
163
            child_pref_h = child.get_preferred_height()[0]
 
164
            w = max(w, child_pref_w)
 
165
            h = max(h, child_pref_h)
 
166
 
 
167
        self._cell_size = (w, h)
 
168
        return w, h
 
169
 
 
170
    def set_row_spacing(self, value):
 
171
        self.row_spacing = value
 
172
        return
 
173
 
 
174
    def set_column_spacing(self, value):
 
175
        self.column_spacing = value
 
176
        self._layout_children(self.get_allocation())
 
177
        return
 
178
 
 
179
    def remove_all(self):
 
180
        self._cell_size = None
 
181
        for child in self:
 
182
            self.remove(child)
 
183
        return
 
184
 
 
185
# first tier of caching, cache component assets from which frames are
 
186
# rendered
 
187
_frame_asset_cache = {}
 
188
class Frame(Gtk.Alignment):
 
189
 
 
190
    BORDER_RADIUS = 8
 
191
    ASSET_TAG = "default"
 
192
    BORDER_IMAGE = os.path.join(
 
193
        softwarecenter.paths.datadir, "ui/gtk3/art/frame-border-image.png")
 
194
    #~ CORNER_LABEL = os.path.join(
 
195
        #~ softwarecenter.paths.datadir, "ui/gtk3/art/corner-label.png")
 
196
 
 
197
    def __init__(self, padding=0):
 
198
        Gtk.Alignment.__init__(self)
 
199
        # set padding + some additional padding in the bottom, left and
 
200
        # right edges to factor in the dropshadow width, and ensure even
 
201
        # visual border
 
202
        self.set_padding(padding, padding+2, padding+1, padding+1)
 
203
 
 
204
        # corner lable jazz
 
205
        #~ self.show_corner_label = False
 
206
        #~ self.layout = self.create_pango_layout("")
 
207
        #~ self.layout.set_width(40960)
 
208
        #~ self.layout.set_ellipsize(Pango.EllipsizeMode.END)
 
209
 
 
210
        #~ assets = self._cache_art_assets()
 
211
        self._cache_art_assets()
 
212
        # second tier of caching, cache resultant surface of
 
213
        # fully composed and rendered frame
 
214
        self._frame_surface_cache = None
 
215
        #~ self.connect_after("draw", self.on_draw_after,
 
216
                           #~ assets, self.layout)
 
217
        self._allocation = Gdk.Rectangle()
 
218
        self.connect("size-allocate", self.on_size_allocate)
 
219
        self.connect("style-updated", self.on_style_updated)
 
220
        return
 
221
 
 
222
    def on_style_updated(self, widget):
 
223
        self._frame_surface_cache = None
 
224
        return
 
225
 
 
226
    def on_size_allocate(self, *args):
 
227
        old = self._allocation
 
228
        cur = self.get_allocation()
 
229
        if cur.width != old.width or cur.height != old.height:
 
230
            self._frame_surface_cache = None
 
231
            self._allocation = cur
 
232
            return
 
233
        return True
 
234
 
 
235
    def _cache_art_assets(self):
 
236
        global _frame_asset_cache
 
237
        at = self.ASSET_TAG
 
238
        assets = _frame_asset_cache
 
239
        if at in assets: return assets
 
240
 
 
241
        def cache_corner_surface(tag, xo, yo):
 
242
            sw = sh = cnr_slice
 
243
            surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, sw, sh)
 
244
            cr = cairo.Context(surf)
 
245
            cr.set_source_surface(border_image, xo, yo)
 
246
            cr.paint()
 
247
            assets[tag] = surf
 
248
            del cr
 
249
            return
 
250
 
 
251
        def cache_edge_pattern(tag, xo, yo, sw, sh):
 
252
            surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, sw, sh)
 
253
            cr = cairo.Context(surf)
 
254
            cr.set_source_surface(border_image, xo, yo)
 
255
            cr.paint()
 
256
            ptrn = cairo.SurfacePattern(surf)
 
257
            ptrn.set_extend(cairo.EXTEND_REPEAT)
 
258
            assets[tag] = ptrn
 
259
            del cr
 
260
            return
 
261
 
 
262
        # register the asset tag within the asset_cache
 
263
        assets[at] = 'loaded'
 
264
 
 
265
        # the basic stuff
 
266
        border_image = cairo.ImageSurface.create_from_png(self.BORDER_IMAGE)
 
267
        assets["corner-slice"] = cnr_slice = 10
 
268
        w = border_image.get_width()
 
269
        h = border_image.get_height()
 
270
 
 
271
        # caching ....
 
272
        # north-west corner of border image
 
273
        cache_corner_surface("%s-nw" % at, 0, 0)
 
274
        # northern edge pattern
 
275
        cache_edge_pattern("%s-n" % at,
 
276
                           -cnr_slice, 0,
 
277
                           w-2*cnr_slice, cnr_slice)
 
278
        # north-east corner
 
279
        cache_corner_surface("%s-ne" % at, -(w-cnr_slice), 0)
 
280
        # eastern edge pattern
 
281
        cache_edge_pattern("%s-e" % at,
 
282
                           -(w-cnr_slice), -cnr_slice,
 
283
                           cnr_slice, h-2*cnr_slice)
 
284
        # south-east corner
 
285
        cache_corner_surface("%s-se" % at, -(w-cnr_slice), -(h-cnr_slice))
 
286
        # southern edge pattern
 
287
        cache_edge_pattern("%s-s" % at,
 
288
                           -cnr_slice, -(h-cnr_slice),
 
289
                           w-2*cnr_slice, cnr_slice)
 
290
        # south-west corner
 
291
        cache_corner_surface("%s-sw" % at, 0, -(h-cnr_slice))
 
292
        # western edge pattern
 
293
        cache_edge_pattern("%s-w" % at, 0, -cnr_slice,
 
294
                           cnr_slice, h-2*cnr_slice)
 
295
        # all done!
 
296
        return assets
 
297
 
 
298
    def do_draw(self, cr):
 
299
        cr.save()
 
300
        self.on_draw(cr)
 
301
        cr.restore()
 
302
        return
 
303
 
 
304
    def on_draw(self, cr):
 
305
        a = self.get_allocation()
 
306
        self.render_frame(cr, a, self.BORDER_RADIUS, _frame_asset_cache)
 
307
 
 
308
        for child in self: self.propagate_draw(child, cr)
 
309
        return
 
310
 
 
311
    #~ def on_draw_after(self, widget, cr, assets, layout):
 
312
        #~ if not self.show_corner_label: return
 
313
        #~ cr.save()
 
314
        #~ surf = assets["corner-label"]
 
315
        #~ w = surf.get_width()
 
316
        #~ h = surf.get_height()
 
317
        #~ cr.reset_clip()
 
318
        #~ # the following arbitrary adjustments are specific to the
 
319
        #~ # corner-label.png image...
 
320
 
 
321
        #~ # alter the to allow drawing outside of the widget bounds
 
322
        #~ cr.rectangle(-10, -10, w+4, h+4)
 
323
        #~ cr.clip()
 
324
        #~ cr.set_source_surface(surf, -7, -8)
 
325
        #~ cr.paint()
 
326
        #~ # render label
 
327
        #~ ex = layout.get_pixel_extents()[1]
 
328
        #~ # transalate to the visual center of the corner-label
 
329
        #~ cr.translate(19, 18)
 
330
        #~ # rotate counter-clockwise
 
331
        #~ cr.rotate(-pi*0.25)
 
332
        #~ # paint normal markup
 
333
        #~ Gtk.render_layout(widget.get_style_context(),
 
334
                          #~ cr, -ex.width/2, -ex.height/2, layout)
 
335
        #~ cr.restore()
 
336
        #~ return
 
337
 
 
338
    def set_show_corner_label(self, show_label):
 
339
        if (not self.layout.get_text() and
 
340
            self.show_corner_label == show_label): return
 
341
        global _frame_asset_cache
 
342
        assets = _frame_asset_cache
 
343
 
 
344
        if "corner-label" not in assets:
 
345
            # cache corner label
 
346
            surf = cairo.ImageSurface.create_from_png(self.CORNER_LABEL)
 
347
            assets["corner-label"] = surf
 
348
 
 
349
        self.show_corner_label = show_label
 
350
        self.queue_draw()
 
351
        return
 
352
 
 
353
    #~ def set_corner_label(self, markup):
 
354
        #~ markup = '<span font_desc="12" color="white"><b>%s</b></span>' % markup
 
355
        #~ self.set_show_corner_label(True)
 
356
        #~ self.layout.set_markup(markup, -1)
 
357
        #~ self.queue_draw()
 
358
        #~ return
 
359
 
 
360
    def render_frame(self, cr, a, border_radius, assets):
 
361
        # we cache as much of the drawing as possible
 
362
        # store a copy of the rendered frame surface, so we only have to
 
363
        # do a full redraw if the widget dimensions change
 
364
        if self._frame_surface_cache is None:
 
365
            surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, a.width, a.height)
 
366
            _cr = cairo.Context(surf)
 
367
 
 
368
            at = self.ASSET_TAG
 
369
 
 
370
            width = a.width
 
371
            height = a.height
 
372
            cnr_slice = assets["corner-slice"]
 
373
 
 
374
            # paint north-west corner
 
375
            _cr.set_source_surface(assets["%s-nw" % at], 0, 0)
 
376
            _cr.paint()
 
377
 
 
378
            # paint north length
 
379
            _cr.save()
 
380
            _cr.set_source(assets["%s-n" % at])
 
381
            _cr.rectangle(cnr_slice, 0, width-2*cnr_slice, cnr_slice)
 
382
            _cr.clip()
 
383
            _cr.paint()
 
384
            _cr.restore()
 
385
 
 
386
            # paint north-east corner
 
387
            _cr.set_source_surface(assets["%s-ne" % at],
 
388
                                   width-cnr_slice, 0)
 
389
            _cr.paint()
 
390
 
 
391
            # paint east length
 
392
            _cr.save()
 
393
            _cr.translate(width-cnr_slice, cnr_slice)
 
394
            _cr.set_source(assets["%s-e" % at])
 
395
            _cr.rectangle(0, 0, cnr_slice, height-2*cnr_slice)
 
396
            _cr.clip()
 
397
            _cr.paint()
 
398
            _cr.restore()
 
399
 
 
400
            # paint south-east corner
 
401
            _cr.set_source_surface(assets["%s-se" % at],
 
402
                                   width-cnr_slice,
 
403
                                   height-cnr_slice)
 
404
            _cr.paint()
 
405
 
 
406
            # paint south length
 
407
            _cr.save()
 
408
            _cr.translate(cnr_slice, height-cnr_slice)
 
409
            _cr.set_source(assets["%s-s" % at])
 
410
            _cr.rectangle(0, 0, width-2*cnr_slice, cnr_slice)
 
411
            _cr.clip()
 
412
            _cr.paint()
 
413
            _cr.restore()
 
414
 
 
415
            # paint south-west corner
 
416
            _cr.set_source_surface(assets["%s-sw" % at],
 
417
                                   0, height-cnr_slice)
 
418
            _cr.paint()
 
419
 
 
420
            # paint west length
 
421
            _cr.save()
 
422
            _cr.translate(0, cnr_slice)
 
423
            _cr.set_source(assets["%s-w" % at])
 
424
            _cr.rectangle(0, 0, cnr_slice, height-2*cnr_slice)
 
425
            _cr.clip()
 
426
            _cr.paint()
 
427
            _cr.restore()
 
428
 
 
429
            # fill interior
 
430
            rounded_rect(_cr, 3, 2, a.width-6, a.height-6, border_radius)
 
431
            context = self.get_style_context()
 
432
            bg = context.get_background_color(self.get_state_flags())
 
433
 
 
434
            Gdk.cairo_set_source_rgba(_cr, bg)
 
435
            _cr.fill_preserve()
 
436
 
 
437
            lin = cairo.LinearGradient(0, 0, 0, max(300, a.height))
 
438
            lin.add_color_stop_rgba(0, 1, 1, 1, 0.02)
 
439
            lin.add_color_stop_rgba(1, 0, 0, 0, 0.06)
 
440
            _cr.set_source(lin)
 
441
            _cr.fill()
 
442
 
 
443
            self._frame_surface_cache = surf
 
444
            del _cr
 
445
 
 
446
        # paint the cached surface and apply a rounded rect clip to
 
447
        # child draw ops
 
448
        A = self.get_allocation()
 
449
        xo, yo = a.x-A.x, a.y-A.y
 
450
 
 
451
        cr.set_source_surface(self._frame_surface_cache, xo, yo)
 
452
        cr.paint()
 
453
 
 
454
        #~ rounded_rect(cr, xo+3, yo+2, a.width-6, a.height-6, border_radius)
 
455
        #~ cr.clip()
 
456
        return
 
457
 
 
458
 
 
459
class SmallBorderRadiusFrame(Frame):
 
460
 
 
461
    BORDER_RADIUS = 3
 
462
    ASSET_TAG = "small"
 
463
    BORDER_IMAGE = os.path.join(
 
464
        softwarecenter.paths.datadir, "ui/gtk3/art/frame-border-image-2px-border-radius.png")
 
465
 
 
466
    def __init__(self, padding=3):
 
467
        Frame.__init__(self, padding)
 
468
        return
 
469
 
 
470
 
 
471
class FramedBox(Frame):
 
472
 
 
473
    def __init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=0, padding=0):
 
474
        Frame.__init__(self, padding)
 
475
        self.box = Gtk.Box.new(orientation, spacing)
 
476
        Gtk.Alignment.add(self, self.box)
 
477
        return
 
478
 
 
479
    def add(self, *args, **kwargs):
 
480
        return self.box.add(*args, **kwargs)
 
481
 
 
482
    def pack_start(self, *args, **kwargs):
 
483
        return self.box.pack_start(*args, **kwargs)
 
484
 
 
485
    def pack_end(self, *args, **kwargs):
 
486
        return self.box.pack_end(*args, **kwargs)
 
487
 
 
488
 
 
489
class HeaderPosition:
 
490
    LEFT = 0.0
 
491
    CENTER = 0.5
 
492
    RIGHT = 1.0
 
493
 
 
494
 
 
495
class FramedHeaderBox(FramedBox):
 
496
 
 
497
    MARKUP = '<b>%s</b>'
 
498
 
 
499
    def __init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=0, padding=0):
 
500
        FramedBox.__init__(self, Gtk.Orientation.VERTICAL, spacing, padding)
 
501
        self.header = Gtk.Box.new(Gtk.Orientation.HORIZONTAL, spacing)
 
502
        self.header_alignment = Gtk.Alignment()
 
503
        self.header_alignment.add(self.header)
 
504
        self.box.pack_start(self.header_alignment, False, False, 0)
 
505
        self.content_box = Gtk.Box.new(orientation, spacing)
 
506
        self.box.add(self.content_box)
 
507
        return
 
508
 
 
509
    def on_draw(self, cr):
 
510
        a = self.get_allocation()
 
511
        self.render_frame(cr, a, Frame.BORDER_RADIUS, _frame_asset_cache)
 
512
        a = self.header_alignment.get_allocation()
 
513
        self.render_header(cr, a, Frame.BORDER_RADIUS, _frame_asset_cache)
 
514
 
 
515
        for child in self: self.propagate_draw(child, cr)
 
516
        return
 
517
 
 
518
    def add(self, *args, **kwargs):
 
519
        return self.content_box.add(*args, **kwargs)
 
520
 
 
521
    def pack_start(self, *args, **kwargs):
 
522
        return self.content_box.pack_start(*args, **kwargs)
 
523
 
 
524
    def pack_end(self, *args, **kwargs):
 
525
        return self.content_box.pack_end(*args, **kwargs)
 
526
 
 
527
    # XXX: non-functional with current code...
 
528
    #~ def set_header_expand(self, expand):
 
529
        #~ alignment = self.header_alignment
 
530
        #~ if expand:
 
531
            #~ expand = 1.0
 
532
        #~ else:
 
533
            #~ expand = 0.0
 
534
        #~ alignment.set(alignment.get_property("xalign"),
 
535
                      #~ alignment.get_property("yalign"),
 
536
                      #~ expand, 1.0)
 
537
 
 
538
    def set_header_position(self, position):
 
539
        alignment = self.header_alignment
 
540
        alignment.set(position, 0.5,
 
541
                      alignment.get_property("xscale"),
 
542
                      alignment.get_property("yscale"))
 
543
 
 
544
    def set_header_label(self, label):
 
545
        if not hasattr(self, "title"):
 
546
            self.title = Gtk.Label()
 
547
            self.title.set_padding(StockEms.MEDIUM, StockEms.SMALL)
 
548
            context = self.title.get_style_context()
 
549
            context.add_class("frame-header-title")
 
550
            self.header.pack_start(self.title, False, False, 0)
 
551
            self.title.show()
 
552
 
 
553
        self.title.set_markup(self.MARKUP % label)
 
554
        return
 
555
 
 
556
    def header_implements_more_button(self, callback=None):
 
557
        if not hasattr(self, "more"):
 
558
            self.more = MoreLink()
 
559
            self.header.pack_end(self.more, False, False, 0)
 
560
        return
 
561
 
 
562
    def render_header(self, cr, a, border_radius, assets):
 
563
 
 
564
        if hasattr(self, "more"):
 
565
            context = self.get_style_context()
 
566
 
 
567
            # set the arrow fill color
 
568
            context = self.more.get_style_context()
 
569
            cr.save()
 
570
 
 
571
            bg = context.get_background_color(self.get_state_flags())
 
572
            Gdk.cairo_set_source_rgba(cr, bg)
 
573
 
 
574
            # the arrow shape stuff
 
575
            r = Frame.BORDER_RADIUS - 1
 
576
            ta = self.more.get_allocation()
 
577
 
 
578
            y = ta.y - a.y + 2
 
579
            h = ta.height - 2
 
580
 
 
581
            if self.get_direction() == Gtk.TextDirection.RTL:
 
582
                x = ta.x - a.x + 3
 
583
                w = ta.width + StockEms.MEDIUM
 
584
 
 
585
                cr.new_sub_path()
 
586
                cr.arc(r+x, r+y, r, PI, 270*PI_OVER_180)
 
587
                cr.line_to(x+w, y)
 
588
                cr.line_to(x+w - StockEms.MEDIUM, y + h/2)
 
589
                cr.line_to(x+w, y+h)
 
590
                cr.line_to(x, y+h)
 
591
                cr.close_path()
 
592
 
 
593
                cr.fill()
 
594
 
 
595
                cr.move_to(x+w, y)
 
596
                cr.line_to(x+w - StockEms.MEDIUM, y + h/2)
 
597
                cr.line_to(x+w, y+h)
 
598
 
 
599
            else:
 
600
                x = ta.x - a.x - StockEms.MEDIUM
 
601
                w = ta.width + StockEms.MEDIUM - 1
 
602
 
 
603
                cr.move_to(x, y)
 
604
                cr.arc(x+w-r, y+r, r, 270*PI_OVER_180, 0)
 
605
                cr.line_to(x+w, y+h)
 
606
                cr.line_to(x, y+h)
 
607
                cr.line_to(x + StockEms.MEDIUM, y + h/2)
 
608
                cr.close_path()
 
609
 
 
610
                cr.fill()
 
611
 
 
612
                cr.move_to(x, y)
 
613
                cr.line_to(x + StockEms.MEDIUM, y + h/2)
 
614
                cr.line_to(x, y+h)
 
615
 
 
616
            bc = context.get_border_color(self.get_state_flags())
 
617
            Gdk.cairo_set_source_rgba(cr, bc)
 
618
            cr.set_line_width(1)
 
619
            cr.stroke()
 
620
 
 
621
            cr.restore()
 
622
 
 
623
        # paint the containers children
 
624
        for child in self: self.propagate_draw(child, cr)
 
625
        return
 
626
 
 
627
 
 
628
# this is used in the automatic tests
 
629
def get_test_container_window():
 
630
    win = Gtk.Window()
 
631
    win.set_size_request(500, 300)
 
632
    f = FlowableGrid()
 
633
 
 
634
    import buttons
 
635
 
 
636
    for i in range(10):
 
637
        t = buttons.CategoryTile("test", "folder")
 
638
        f.add_child(t)
 
639
 
 
640
    scroll = Gtk.ScrolledWindow()
 
641
    scroll.add_with_viewport(f)
 
642
 
 
643
    win.add(scroll)
 
644
    win.show_all()
 
645
 
 
646
    win.connect("destroy", lambda x: Gtk.main_quit())
 
647
    return win
 
648
 
 
649
if __name__ == '__main__':
 
650
    win = get_test_container_window()
 
651
    win.show_all()
 
652
    Gtk.main()