~gary-lasker/software-center/installedview-search-fixes

« back to all changes in this revision

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

mergedĀ fromĀ lp:~alexeftimie/software-center/backend-refactor

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
# this program; if not, write to the Free Software Foundation, Inc.,
17
17
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
18
 
 
19
import cairo
 
20
import os
 
21
from gettext import gettext as _
 
22
 
19
23
from gi.repository import Gtk
20
24
from gi.repository import Gdk
21
25
from gi.repository import GObject
22
26
from gi.repository import GdkPixbuf
 
27
from gi.repository import WebKit
23
28
 
24
29
from softwarecenter.utils import SimpleFileDownloader
25
 
 
26
 
 
27
 
class Exhibit(Gtk.EventBox):
28
 
    """ a single exhibit ui element """
29
 
 
30
 
    __gsignals__ = {
31
 
        "clicked" : (GObject.SignalFlags.RUN_LAST,
32
 
                     None, 
33
 
                     (),
34
 
                     )
35
 
        }
36
 
 
37
 
    def __init__(self, exhibit, right_pixel_cutoff=0):
 
30
from softwarecenter.ui.gtk3.em import StockEms
 
31
from softwarecenter.ui.gtk3.shapes import Circle
 
32
import softwarecenter.paths
 
33
 
 
34
_asset_cache = {}
 
35
 
 
36
EXHIBIT_HTML = """
 
37
<html><head>
 
38
<style type="text/css">
 
39
.banner_text {
 
40
font-size:1.7em;
 
41
color:white;
 
42
background:yellow;
 
43
padding: 0.2em;
 
44
text-shadow:0em 0em 0.075em black;
 
45
position:absolute;
 
46
top:30;
 
47
left:20;
 
48
}
 
49
.banner_subtext {
 
50
font-size:1.2em;
 
51
color:white;
 
52
padding: 1em;
 
53
text-shadow:0em 0em 0.075em black;
 
54
position:absolute;
 
55
top:90;
 
56
left:30;
 
57
}
 
58
</style>
 
59
</head><body>
 
60
 
 
61
<img style="position:absolute; top:0; left:0;" src="%(banner_url)s">
 
62
<div class="banner_text">%(title)s</div>
 
63
<div class="banner_subtext"> %(subtitle)s</div>
 
64
 
 
65
</body></html>
 
66
"""
 
67
 
 
68
class DefaultExhibit(object):
 
69
    def __init__(self):
 
70
        self.id = 0
 
71
        self.package_names = ""
 
72
        self.title_translated = "Default exhibit"
 
73
        self.published = True
 
74
        self.banner_url = "file://%s" % (os.path.abspath(os.path.join(softwarecenter.paths.datadir, "default_banner/fallback.png")))
 
75
        self.html = EXHIBIT_HTML % { 
 
76
            'banner_url' : self.banner_url,
 
77
            'title' : _("Welcome to the Ubuntu Software Center"),
 
78
            'subtitle' : _("Its a new day"),
 
79
      }
 
80
        # we should extract this automatically from the html
 
81
        #self.atk_name = _("Default Banner")
 
82
        #self.atk_description = _("You see this banner because you have no cached banners")
 
83
 
 
84
class FeaturedExhibit(object):
 
85
    def __init__(self):
 
86
        self.id = 0
 
87
        self.package_names = "armagetronad,calibre,cheese,homebank,stellarium,gimp,inkscape,blender,audacity,gufw,frozen-bubble,fretsonfire,moovida,liferea,arista,gtg,freeciv-client-gtk,supertuxkart,tumiki-fighters,tuxpaint,webservice-office-zoho"
 
88
        self.title_translated = "Featured exhibit"
 
89
        self.published = True
 
90
        self.banner_url = "file://%s" % (os.path.abspath(os.path.join(softwarecenter.paths.datadir, "default_banner/fallback2.png")))
 
91
        self.html = EXHIBIT_HTML % { 
 
92
            'banner_url' : self.banner_url,
 
93
            'title' : _("Stuff we like"),
 
94
            'subtitle' : _("Its just great, try it!"),
 
95
      }
 
96
        # we should extract this automatically from the html
 
97
        #self.atk_name = _("Default Banner")
 
98
        #self.atk_description = _("You see this banner because you have no cached banners")
 
99
 
 
100
 
 
101
class _HtmlRenderer(Gtk.OffscreenWindow):
 
102
 
 
103
    __gsignals__ = {
 
104
        "render-finished" : (GObject.SignalFlags.RUN_LAST,
 
105
                            None, 
 
106
                            (),
 
107
                           )
 
108
        }
 
109
 
 
110
 
 
111
    def __init__(self):
 
112
        Gtk.OffscreenWindow.__init__(self)
 
113
        self.view = WebKit.WebView()
 
114
        settings = self.view.get_settings()
 
115
        settings.set_property("enable-java-applet", False)
 
116
        settings.set_property("enable-plugins", False)
 
117
        settings.set_property("enable-scripts", False)
 
118
        self.view.set_size_request(-1, ExhibitBanner.MAX_HEIGHT)
 
119
        self.add(self.view)
 
120
        self.show_all()
 
121
        self.loader = SimpleFileDownloader()
 
122
        self.loader.connect("file-download-complete",
 
123
                            self.on_download_complete)
 
124
        self.exhibit = None
 
125
        self.view.connect("notify::load-status", self._on_load_status)
 
126
        return
 
127
 
 
128
    def _on_load_status(self, view, prop):
 
129
        if view.get_property("load-status") ==  WebKit.LoadStatus.FINISHED:
 
130
            # this needs to run with a timeout because otherwise the 
 
131
            # status is emited before the offscreen image is finihsed
 
132
            GObject.timeout_add(1, lambda: self.emit("render-finished"))
 
133
 
 
134
    def on_download_complete(self, loader, path):
 
135
        image_name = os.path.basename(path)
 
136
        cache_dir = os.path.dirname(path)
 
137
        if hasattr(self.exhibit, "html") and self.exhibit.html:
 
138
            html = self.exhibit.html
 
139
        else:
 
140
            # special case, if there is no title, then the package_names
 
141
            # is actually just a single string
 
142
            if (not hasattr(self.exhibit, "title_translated") or
 
143
                not self.exhibit.title_translated):
 
144
                self.exhibit.title_translated = self.exhibit.package_names
 
145
            html = EXHIBIT_HTML % { 'banner_url' : self.exhibit.banner_url,
 
146
                                    'title' : self.exhibit.title_translated,
 
147
                                    'subtitle' : "",
 
148
                                    }
 
149
        html = html.replace(self.exhibit.banner_url, image_name)
 
150
        self.view.load_string(html, "text/html", "UTF-8", 
 
151
                              "file:///%s/" % cache_dir)
 
152
        return
 
153
 
 
154
    def set_exhibit(self, exhibit):
 
155
        self.exhibit = exhibit
 
156
        self.loader.download_file(exhibit.banner_url, 
 
157
                                  use_cache=True,
 
158
                                  simple_quoting_for_webkit=True)
 
159
        return 
 
160
 
 
161
 
 
162
class ExhibitButton(Gtk.Button):
 
163
 
 
164
    def __init__(self):
 
165
        Gtk.Button.__init__(self)
 
166
        self.DROPSHADOW = GdkPixbuf.Pixbuf.new_from_file(
 
167
            os.path.join(softwarecenter.paths.datadir,
 
168
                         "ui/gtk3/art/circle-dropshadow.png"))
 
169
        self.set_focus_on_click(False)
 
170
        self.set_name("exhibit-button")
 
171
        self._dropshadow = None
 
172
        # is the current active "page"
 
173
        self.is_active = False
 
174
        self.connect("size-allocate", self.on_size_allocate)
 
175
 
 
176
    def on_size_allocate(self, *args):
 
177
        a = self.get_allocation()
 
178
        if (self._dropshadow is not None and
 
179
            a.width == self._dropshadow.get_width() and
 
180
            a.height == self._dropshadow.get_height()):
 
181
            return
 
182
 
 
183
        self._dropshadow = self.DROPSHADOW.scale_simple(
 
184
                        a.width, a.width, GdkPixbuf.InterpType.BILINEAR)
 
185
        self._margin = int(float(a.width) / self.DROPSHADOW.get_width() * 15)
 
186
        return
 
187
 
 
188
    def do_draw(self, cr):
 
189
        a = self.get_allocation()
 
190
        #state = self.get_state_flags()
 
191
        context = self.get_style_context()
 
192
 
 
193
        ds_h = self._dropshadow.get_height()
 
194
        y = (a.height - ds_h) / 2
 
195
        Gdk.cairo_set_source_pixbuf(cr, self._dropshadow, 0, y)
 
196
        cr.paint()
 
197
        Circle.layout(cr, self._margin, (a.height-ds_h)/2 + self._margin,
 
198
                      a.width-2*self._margin,
 
199
                      a.width-2*self._margin)
 
200
        if self.is_active:
 
201
            color = context.get_background_color(Gtk.StateFlags.SELECTED)
 
202
        else:
 
203
            color = context.get_background_color(Gtk.StateFlags.INSENSITIVE)
 
204
 
 
205
        Gdk.cairo_set_source_rgba(cr, color)
 
206
        cr.fill()
 
207
 
 
208
        for child in self: 
 
209
            self.propagate_draw(child, cr)
 
210
        return
 
211
 
 
212
 
 
213
class ExhibitArrowButton(ExhibitButton):
 
214
 
 
215
    def __init__(self, arrow_type, shadow_type=Gtk.ShadowType.IN):
 
216
        ExhibitButton.__init__(self)
 
217
        a = Gtk.Alignment()
 
218
        a.set_padding(1,1,1,1)
 
219
        a.add(Gtk.Arrow.new(arrow_type, shadow_type))        
 
220
        self.add(a)
 
221
        return
 
222
 
 
223
 
 
224
class ExhibitBanner(Gtk.EventBox):
 
225
 
 
226
    __gsignals__ = {
 
227
        "show-exhibits-clicked" : (GObject.SignalFlags.RUN_LAST,
 
228
                           None, 
 
229
                           (GObject.TYPE_PYOBJECT,),
 
230
                           )
 
231
        }
 
232
 
 
233
    DROPSHADOW_HEIGHT = 11
 
234
    MAX_HEIGHT = 200 # pixels
 
235
    TIMEOUT_SECONDS = 15
 
236
 
 
237
    def __init__(self):
38
238
        Gtk.EventBox.__init__(self)
39
 
        self.fixed = Gtk.Fixed()
40
 
        self.image = Gtk.Image()
41
 
        self.label = Gtk.Label()
42
 
        self.downloader = SimpleFileDownloader()
43
 
        self.downloader.connect(
44
 
            "file-download-complete", self._on_file_download_complete)
45
 
        self.add(self.fixed)
46
 
        self.exhibit = None
47
 
        self.set_right_pixel_cutoff(right_pixel_cutoff)
 
239
        vbox = Gtk.VBox()
 
240
        vbox.set_border_width(StockEms.SMALL)
 
241
        self.add(vbox)
 
242
 
 
243
        # defined to make overriding softwarecenter.paths.datadir possible
 
244
        self.NORTHERN_DROPSHADOW = os.path.join(
 
245
            softwarecenter.paths.datadir,
 
246
            "ui/gtk3/art/exhibit-dropshadow-n.png")
 
247
        self.SOUTHERN_DROPSHADOW = os.path.join(
 
248
            softwarecenter.paths.datadir,
 
249
            "ui/gtk3/art/exhibit-dropshadow-s.png")
 
250
        self.FALLBACK = os.path.join(
 
251
            softwarecenter.paths.datadir,
 
252
            "default_banner/fallback.png")
 
253
 
 
254
        hbox = Gtk.HBox(spacing=StockEms.SMALL)
 
255
        vbox.pack_end(hbox, False, False, 0)
 
256
 
 
257
        next = ExhibitArrowButton(Gtk.ArrowType.RIGHT)
 
258
        previous = ExhibitArrowButton(Gtk.ArrowType.LEFT)
 
259
        self.nextprev_hbox = Gtk.HBox()
 
260
        self.nextprev_hbox.pack_start(previous, False, False, 0)
 
261
        self.nextprev_hbox.pack_start(next, False, False, 0)
 
262
        hbox.pack_end(self.nextprev_hbox, False, False, 0)
 
263
 
 
264
        self.index_hbox = Gtk.HBox(spacing=StockEms.SMALL)
 
265
        alignment = Gtk.Alignment.new(1.0, 1.0, 0.0, 1.0)
 
266
        alignment.add(self.index_hbox)
 
267
        hbox.pack_end(alignment, False, False, 0)
 
268
 
 
269
        self.cursor = 0
 
270
        self._timeout = 0
 
271
 
 
272
        self.alpha = 1.0
 
273
        self.image = None 
 
274
        self.old_image = None 
 
275
        self.renderer = _HtmlRenderer()
 
276
        self.renderer.connect("render-finished", self.on_banner_rendered)
 
277
 
 
278
        self.set_visible_window(False)
 
279
        self.set_size_request(-1, self.MAX_HEIGHT)
 
280
        self.exhibits = []
 
281
 
 
282
        next.connect('clicked', self.on_next_clicked)
 
283
        previous.connect('clicked', self.on_previous_clicked)
 
284
 
 
285
        self._dotsigs = []
 
286
 
 
287
        self._cache_art_assets()
48
288
        self._init_event_handling()
49
 
        self._set_exhibit(exhibit)
50
 
        self.connect("draw", self.on_draw)
51
 
 
52
 
    def on_draw(self, widget, cr):
53
 
        Gdk.cairo_set_source_rgba(cr, self.bgcolor)
54
 
        cr.paint()
55
 
        return
56
 
 
57
 
    def set_right_pixel_cutoff(self, right_pixel_cutoff):
58
 
        self.right_pixel_cutoff=right_pixel_cutoff
59
 
        
60
 
    def _set_exhibit(self, exhibit):
61
 
        self.exhibit_data = exhibit
62
 
        self.bgcolor = Gdk.RGBA()
63
 
        self.bgcolor.parse(exhibit.background_color)
64
 
        # FIXME:
65
 
        # - set background color
66
 
        # background image first
67
 
        self.downloader.download_file(exhibit.banner_url, use_cache=True)
68
 
        self.fixed.put(self.image, 0, 0)
69
 
        # then label on top
70
 
        self.label.set_text(exhibit.title_translated)
71
 
        self.fixed.put(
72
 
            self.label, exhibit.title_coords[0], exhibit.title_coords[1])
73
 
        # FIXME: set font name, colors, size (that is not exposed in the API)
74
289
 
75
290
    def _init_event_handling(self):
76
 
        self.set_property("can-focus", True)
 
291
        self.set_can_focus(True)
77
292
        self.set_events(Gdk.EventMask.BUTTON_RELEASE_MASK|
78
293
                        Gdk.EventMask.ENTER_NOTIFY_MASK|
79
294
                        Gdk.EventMask.LEAVE_NOTIFY_MASK)
80
 
 
81
 
        self.connect("enter-notify-event", self._on_enter_notify)
82
 
        self.connect("leave-notify-event", self._on_leave_notify)
83
 
        self.connect("button-release-event", self._on_button_release)
84
 
 
85
 
    def _on_enter_notify(self, widget, event):
86
 
        window = self.get_window()
87
 
        if window:
88
 
            window.set_cursor(Gdk.Cursor.new(Gdk.CursorType.HAND2))
89
 
 
90
 
    def _on_leave_notify(self, widget, event):
91
 
        window = self.get_window()
92
 
        if window:
93
 
            window.set_cursor(None)
94
 
 
95
 
    def _on_button_release(self, widget, event):
96
 
        if event.button != 1:
 
295
        #~ self.connect("enter-notify-event", self.on_enter_notify)
 
296
        #~ self.connect("leave-notify-event", self.on_leave_notify)
 
297
        self.connect("button-release-event", self.on_button_release)
 
298
 
 
299
    def on_enter_notify(self, *args):
 
300
        return
 
301
 
 
302
    def on_leave_notify(self, *args):
 
303
        return
 
304
 
 
305
    def on_button_release(self, *args):
 
306
        exhibit = self.exhibits[self.cursor]
 
307
        if exhibit.package_names:
 
308
            self.emit("show-exhibits-clicked", exhibit)
 
309
        return
 
310
 
 
311
    def on_next_clicked(self, *args):
 
312
        self.next_exhibit()
 
313
        self.queue_next()
 
314
        return
 
315
 
 
316
    def on_previous_clicked(self, *args):
 
317
        self.previous()
 
318
        self.queue_next()
 
319
        return
 
320
 
 
321
    def cleanup_timeout(self):
 
322
        if self._timeout > 0:
 
323
            GObject.source_remove(self._timeout)
 
324
            self._timeout = 0
 
325
        return
 
326
 
 
327
    def _render_exhibit_at_cursor(self):
 
328
        # copy old image for the fade
 
329
        if self.image:
 
330
            self.old_image = self.image.copy()
 
331
        # set the rigth one
 
332
        self.renderer.set_exhibit(self.exhibits[self.cursor])
 
333
        # make sure the active button is having a different color
 
334
        for i, w in enumerate(self.index_hbox):
 
335
            w.is_active = (i == self.cursor)
 
336
 
 
337
    def on_paging_dot_clicked(self, dot, index):
 
338
        self.cursor = index
 
339
        self._render_exhibit_at_cursor()
 
340
 
 
341
    # next() is a special function in py3 so we call this next_exhibit
 
342
    def next_exhibit(self):
 
343
        if len(self.exhibits)-1 == self.cursor:
 
344
            self.cursor = 0
 
345
        else:
 
346
            self.cursor += 1
 
347
        self._render_exhibit_at_cursor()
 
348
        return False
 
349
 
 
350
    def previous(self):
 
351
        if self.cursor == 0:
 
352
            self.cursor = len(self.exhibits)-1
 
353
        else:
 
354
            self.cursor -= 1
 
355
        self._render_exhibit_at_cursor()
 
356
        return False
 
357
 
 
358
    def queue_next(self):
 
359
        self.cleanup_timeout()
 
360
        self._timeout = GObject.timeout_add_seconds(
 
361
                                    self.TIMEOUT_SECONDS, self.next_exhibit)
 
362
        return self._timeout
 
363
 
 
364
    def on_banner_rendered(self, renderer):
 
365
        self.image = renderer.get_pixbuf()
 
366
 
 
367
        if self.image.get_width() == 1:
 
368
            # the offscreen window is not really as such content not
 
369
            # correctly rendered
 
370
            GObject.timeout_add(500, self.on_banner_rendered, renderer)
97
371
            return
98
 
        self.emit("clicked")
99
 
 
100
 
    def _on_file_download_complete(self, downloader, path):
101
 
        pb = GdkPixbuf.Pixbuf.new_from_file(path)
102
 
        #~ pb = pb.scale_simple(600, 200, GdkPixbuf.InterpType.BILINEAR)
103
 
        #~ print pb.get_width(), pb.get_height()
104
 
        self.image.set_from_pixbuf(pb)
105
 
 
106
 
    def __repr__(self):
107
 
        return "<Exhibit: '%s'>" % (self.exhibit_data.title_translated)
108
 
 
109
 
 
110
 
class ExhibitBanner(Gtk.Fixed):
111
 
 
112
 
    __gsignals__ = {
113
 
        "show-exhibits" : (GObject.SignalFlags.RUN_LAST,
114
 
                           None, 
115
 
                           (GObject.TYPE_PYOBJECT,),
116
 
                           )
117
 
        }
118
 
 
119
 
    def __init__(self):
120
 
        Gtk.Fixed.__init__(self)
121
 
        self.exhibits = []
 
372
 
 
373
        self._fade_in()
 
374
        self.queue_next()
 
375
        return False
 
376
 
 
377
    def _fade_in(self, step=0.05):
 
378
        self.alpha = 0.0
 
379
 
 
380
        def fade_step():
 
381
            retval = True
 
382
            self.alpha += step
 
383
 
 
384
            if self.alpha >= 1.0:
 
385
                self.alpha = 1.0
 
386
                self.old_image = None
 
387
                retval = False
 
388
 
 
389
            self.queue_draw()
 
390
            return retval
 
391
 
 
392
        GObject.timeout_add(50, fade_step)
 
393
        return
 
394
 
 
395
    def _cache_art_assets(self):
 
396
        global _asset_cache
 
397
        assets = _asset_cache
 
398
        if assets: 
 
399
            return assets
 
400
 
 
401
        #~ surf = cairo.ImageSurface.create_from_png(self.NORTHERN_DROPSHADOW)
 
402
        #~ ptrn = cairo.SurfacePattern(surf)
 
403
        #~ ptrn.set_extend(cairo.EXTEND_REPEAT)
 
404
        #~ assets["n"] = ptrn
 
405
 
 
406
        surf = cairo.ImageSurface.create_from_png(self.SOUTHERN_DROPSHADOW)
 
407
        ptrn = cairo.SurfacePattern(surf)
 
408
        ptrn.set_extend(cairo.EXTEND_REPEAT)
 
409
        assets["s"] = ptrn
 
410
        return assets
 
411
 
 
412
    def do_draw(self, cr):
 
413
 
 
414
        # hide the next/prev buttons if needed
 
415
        if len(self.exhibits) == 1:
 
416
            self.nextprev_hbox.hide()
 
417
        else:
 
418
            self.nextprev_hbox.show()
 
419
 
 
420
        # do the actual drawing
 
421
        cr.save()
 
422
 
 
423
        a = self.get_allocation()
 
424
 
 
425
        cr.set_source_rgb(1,1,1)
 
426
        cr.paint()
 
427
 
 
428
        if self.old_image is not None:
 
429
            #x = (a.width - self.old_image.get_width()) / 2
 
430
            x = 0
 
431
            y = 0
 
432
            Gdk.cairo_set_source_pixbuf(cr, self.old_image, x, y)
 
433
            cr.paint()
 
434
 
 
435
        if self.image is not None:
 
436
            #x = (a.width - self.image.get_width()) / 2
 
437
            x = 0
 
438
            y = 0
 
439
            Gdk.cairo_set_source_pixbuf(cr, self.image, x, y)
 
440
            cr.paint_with_alpha(self.alpha)
 
441
 
 
442
        # paint dropshadows last
 
443
        #~ cr.rectangle(0, 0, a.width, self.DROPSHADOW_HEIGHT)
 
444
        #~ cr.clip()
 
445
        #~ cr.set_source(assets["n"])
 
446
        #~ cr.paint()
 
447
        #~ cr.reset_clip()
 
448
 
 
449
        cr.rectangle(0, a.height-self.DROPSHADOW_HEIGHT,
 
450
                     a.width, self.DROPSHADOW_HEIGHT)
 
451
        cr.clip()
 
452
        cr.save()
 
453
        cr.translate(0, a.height-self.DROPSHADOW_HEIGHT)
 
454
        cr.set_source(_asset_cache["s"])
 
455
        cr.paint()
 
456
        cr.restore()
 
457
 
 
458
        cr.set_line_width(1)
 
459
        cr.move_to(-0.5, a.height-0.5)
 
460
        cr.rel_line_to(a.width+1, 0)
 
461
        cr.set_source_rgba(1,1,1,0.75)
 
462
        cr.stroke()
 
463
 
 
464
        cr.restore()
 
465
 
 
466
        for child in self: 
 
467
            self.propagate_draw(child, cr)
 
468
        return
122
469
 
123
470
    def set_exhibits(self, exhibits_list):
124
 
        for exhibit_data in exhibits_list:
125
 
            exhibit = Exhibit(exhibit_data)
126
 
            exhibit.connect("clicked", self._on_exhibit_clicked)
127
 
            self.exhibits.append(exhibit)
128
 
        # now draw them in the self.exhibits order
129
 
        self._draw_exhibits()
130
 
 
131
 
    def _draw_exhibits(self):
132
 
        # remove the old ones
133
 
        self.foreach(lambda w,d: self.remove(w), None)
134
 
 
135
 
        # draw the new ones
136
 
        for (i, exhibit) in enumerate(reversed(self.exhibits)):
137
 
            # FIXME: we may need to put this to the right actually, in the spec
138
 
            #        the wireframe has it on the left, the mockup on the righ
139
 
            self.put(exhibit, i*20, 0)
140
 
 
141
 
    def _on_exhibit_clicked(self, exhibit):
142
 
        if self.exhibits[0] == exhibit:
143
 
            self.emit("show-exhibits", 
144
 
                      exhibit.exhibit_data.package_names.split(","))
145
 
        else:
146
 
            # exchange top with the clicked one
147
 
            self.exhibits[self.exhibits.index(exhibit)] = self.exhibits[0]
148
 
            self.exhibits[0] = exhibit
149
 
            self._draw_exhibits()
150
 
 
151
 
 
152
 
if __name__ == "__main__":
 
471
        self.exhibits = exhibits_list
 
472
        self.cursor = 0
 
473
 
 
474
        for child in self.index_hbox:
 
475
            child.destroy()
 
476
 
 
477
        for sigid in self._dotsigs:
 
478
            GObject.source_remove(sigid)
 
479
 
 
480
        self._dotsigs = []
 
481
        for i, exhibit in enumerate(self.exhibits):
 
482
            dot = ExhibitButton()
 
483
            dot.set_size_request(StockEms.LARGE, StockEms.LARGE)
 
484
            self._dotsigs.append(
 
485
                dot.connect("clicked",
 
486
                            self.on_paging_dot_clicked,
 
487
                            len(self.exhibits) - 1 - i) # index
 
488
            )
 
489
            self.index_hbox.pack_end(dot, False, False, 0)
 
490
            self.index_hbox.show_all()
 
491
 
 
492
        self._render_exhibit_at_cursor()
 
493
        return
 
494
 
 
495
def get_test_exhibits_window():
153
496
    from mock import Mock
154
497
 
155
498
    win = Gtk.Window()
156
 
    win.set_size_request(600, 400)
157
 
 
158
 
    exhibits_list = []
159
 
 
 
499
    win.set_size_request(600, 200)
 
500
 
 
501
    exhibit_banner = ExhibitBanner()
 
502
 
 
503
    exhibits_list = [DefaultExhibit()]
160
504
    for (i, (title, url)) in enumerate([
161
505
            ("1 some title", "https://wiki.ubuntu.com/Brand?action=AttachFile&do=get&target=orangeubuntulogo.png"),
162
506
            ("2 another title", "https://wiki.ubuntu.com/Brand?action=AttachFile&do=get&target=blackeubuntulogo.png"),
163
507
            ("3 yet another title", "https://wiki.ubuntu.com/Brand?action=AttachFile&do=get&target=xubuntu.png"),
164
508
            ]):
165
509
         exhibit = Mock()
166
 
         exhibit.background_color = "#000000"
167
 
         exhibit.banner_url = url
168
 
         exhibit.date_created = "2011-07-20 08:49:15"
169
 
         exhibit.font_color = "#000000"
170
 
         exhibit.font_name = ""
171
 
         exhibit.font_size = 24
172
510
         exhibit.id = i
173
511
         exhibit.package_names = "apt,2vcard"
174
512
         exhibit.published = True
175
 
         exhibit.title_coords = [10, 10]
 
513
         exhibit.style = "some uri to html"
176
514
         exhibit.title_translated = title
 
515
         exhibit.banner_url = url
 
516
         exhibit.html = None
177
517
         exhibits_list.append(exhibit)
178
518
 
179
 
    exhibit_banner = ExhibitBanner()
180
519
    exhibit_banner.set_exhibits(exhibits_list)
181
 
    win.add(exhibit_banner)
 
520
 
 
521
    scroll = Gtk.ScrolledWindow()
 
522
    scroll.add_with_viewport(exhibit_banner)
 
523
    win.add(scroll)
182
524
 
183
525
    win.show_all()
184
526
    win.connect("destroy", Gtk.main_quit)
 
527
    return win
 
528
 
 
529
if __name__ == "__main__":
 
530
    softwarecenter.paths.datadir = "./data"
 
531
    win = get_test_exhibits_window()
185
532
    Gtk.main()