~ubuntu-branches/ubuntu/natty/miro/natty

« back to all changes in this revision

Viewing changes to lib/frontends/widgets/displays.py

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2011-01-22 02:46:33 UTC
  • mfrom: (1.4.10 upstream) (1.7.5 experimental)
  • Revision ID: james.westby@ubuntu.com-20110122024633-kjme8u93y2il5nmf
Tags: 3.5.1-1ubuntu1
* Merge from debian.  Remaining ubuntu changes:
  - Use python 2.7 instead of python 2.6
  - Relax dependency on python-dbus to >= 0.83.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Miro - an RSS based video player application
 
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
 
3
#
 
4
# This program is free software; you can redistribute it and/or modify
 
5
# it under the terms of the GNU General Public License as published by
 
6
# the Free Software Foundation; either version 2 of the License, or
 
7
# (at your option) any later version.
 
8
#
 
9
# This program is distributed in the hope that it will be useful,
 
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
# GNU General Public License for more details.
 
13
#
 
14
# You should have received a copy of the GNU General Public License
 
15
# along with this program; if not, write to the Free Software
 
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
 
17
#
 
18
# In addition, as a special exception, the copyright holders give
 
19
# permission to link the code of portions of this program with the OpenSSL
 
20
# library.
 
21
#
 
22
# You must obey the GNU General Public License in all respects for all of
 
23
# the code used other than OpenSSL. If you modify file(s) with this
 
24
# exception, you may extend this exception to your version of the file(s),
 
25
# but you are not obligated to do so. If you do not wish to do so, delete
 
26
# this exception statement from your version. If you delete this exception
 
27
# statement from all source files in the program, then also delete it here.
 
28
 
 
29
"""displays.py -- Handle switching the content on the right hand side of the
 
30
app.
 
31
"""
 
32
import logging
 
33
 
 
34
import os
 
35
 
 
36
from miro import app
 
37
from miro import messages
 
38
from miro import signals
 
39
from miro import config
 
40
from miro import prefs
 
41
from miro import filetypes
 
42
from miro.gtcache import gettext as _
 
43
from miro.gtcache import ngettext
 
44
from miro.frontends.widgets import browser
 
45
from miro.frontends.widgets import downloadscontroller
 
46
from miro.frontends.widgets import videoconversionscontroller
 
47
from miro.frontends.widgets import feedcontroller
 
48
from miro.frontends.widgets import itemlistcontroller
 
49
from miro.frontends.widgets import playlist
 
50
from miro.frontends.widgets import widgetutil
 
51
from miro.plat.frontends.widgets import widgetset
 
52
 
 
53
class Display(signals.SignalEmitter):
 
54
    """A display is a view that can be shown in the right hand side of the
 
55
    app.
 
56
 
 
57
    Attributes:
 
58
 
 
59
    widget -- Widget to show to the user.
 
60
    """
 
61
 
 
62
    def __init__(self):
 
63
        signals.SignalEmitter.__init__(self)
 
64
        self.create_signal('removed')
 
65
 
 
66
    def on_selected(self):
 
67
        """Perform any code that needs to be run every time the display is
 
68
        selected.
 
69
        """
 
70
        pass
 
71
 
 
72
    def on_activate(self):
 
73
        """Perform code that needs to be run when the display becomes the
 
74
        active display (the one on the top of the display stack).
 
75
        """
 
76
        pass
 
77
 
 
78
    def on_deactivate(self):
 
79
        """Perform code that needs to be run when another display gets pushed
 
80
        on top of this display.
 
81
        """
 
82
        pass
 
83
 
 
84
    def cleanup(self):
 
85
        """Cleanup any resources allocated in create.  This will be called
 
86
        after the widget for this display is removed.
 
87
        """
 
88
        pass
 
89
 
 
90
class TabDisplay(Display):
 
91
    """Display that displays the selection in the tab list."""
 
92
 
 
93
    def __init__(self, tab_type, selected_tabs):
 
94
        raise NotImplementedError()
 
95
 
 
96
    def on_activate(self):
 
97
        app.menu_manager.update_menus()
 
98
 
 
99
    @staticmethod
 
100
    def should_display(tab_type, selected_tabs):
 
101
        """Test if this display should be shown.  """
 
102
        raise NotImplementedError()
 
103
 
 
104
class DisplayManager(object):
 
105
    """Handles managing the display in the right-side of miro.
 
106
 
 
107
    DisplayManagers keep a stack of Displays that are currently is use.  This
 
108
    is used to allow us to switch to a new display, but still keep the old
 
109
    display's state.  For example, when we switch to a video display, we want
 
110
    to keep around the channel display that we switched from and go back to it
 
111
    when the playback is finished.
 
112
    """
 
113
    def __init__(self):
 
114
        self.display_classes = [
 
115
                AudioFeedDisplay,
 
116
                FeedDisplay,
 
117
                PlaylistDisplay,
 
118
                SiteDisplay,
 
119
                SearchDisplay,
 
120
                VideoItemsDisplay,
 
121
                AudioItemsDisplay,
 
122
                OtherItemsDisplay,
 
123
                DownloadingDisplay,
 
124
                VideoConversionsDisplay,
 
125
                GuideDisplay,
 
126
                MultipleSelectionDisplay,
 
127
                DummyDisplay,
 
128
        ]
 
129
        self.display_stack = []
 
130
        self.selected_tab_list = self.selected_tabs = None
 
131
        app.info_updater.connect('sites-removed', SiteDisplay.on_sites_removed)
 
132
 
 
133
    def get_current_display(self):
 
134
        try:
 
135
            return self.display_stack[-1]
 
136
        except IndexError:
 
137
            return None
 
138
    current_display = property(get_current_display)
 
139
 
 
140
    def select_display_for_tabs(self, selected_tab_list, selected_tabs):
 
141
        """Select a display to show in the right-hand side.  """
 
142
        if (selected_tab_list is self.selected_tab_list and
 
143
                selected_tabs == self.selected_tabs and
 
144
                len(self.display_stack) > 0 and
 
145
                isinstance(self.display_stack[-1], TabDisplay)):
 
146
            logging.debug('not reselecting')
 
147
            return
 
148
 
 
149
        self.selected_tab_list = selected_tab_list
 
150
        self.selected_tabs = selected_tabs
 
151
        tab_type = selected_tab_list.type
 
152
 
 
153
        for klass in self.display_classes:
 
154
            if klass.should_display(tab_type, selected_tabs):
 
155
                self.select_display(klass(tab_type, selected_tabs))
 
156
                return
 
157
        raise AssertionError(
 
158
            "Can't find display for %s %s" % (tab_type, selected_tabs))
 
159
 
 
160
    def select_display(self, display):
 
161
        """Select a display and clear out the current display stack."""
 
162
        self.deselect_all_displays()
 
163
        self.push_display(display)
 
164
 
 
165
    def deselect_all_displays(self):
 
166
        """Deselect all displays."""
 
167
        for old_display in self.display_stack:
 
168
            self._unselect_display(old_display)
 
169
        self.display_stack = []
 
170
 
 
171
    def push_display(self, display):
 
172
        """Select a display and push it on top of the display stack"""
 
173
        if len(self.display_stack) > 0:
 
174
            self.current_display.on_deactivate()
 
175
        self.display_stack.append(display)
 
176
        display.on_selected()
 
177
        display.on_activate()
 
178
        app.widgetapp.window.set_main_area(display.widget)
 
179
 
 
180
    def pop_display(self, unselect=True):
 
181
        """Remove the current display, then select the next one in the display
 
182
        stack.
 
183
        """
 
184
        display = self.display_stack.pop()
 
185
        if unselect:
 
186
            self._unselect_display(display)
 
187
        self.current_display.on_activate()
 
188
        app.widgetapp.window.set_main_area(self.current_display.widget)
 
189
 
 
190
    def _unselect_display(self, display):
 
191
        display.on_deactivate()
 
192
        display.cleanup()
 
193
        display.emit("removed")
 
194
 
 
195
    def push_folder_contents_display(self, folder_info):
 
196
        self.push_display(FolderContentsDisplay(folder_info))
 
197
 
 
198
class GuideDisplay(TabDisplay):
 
199
    @staticmethod
 
200
    def should_display(tab_type, selected_tabs):
 
201
        return tab_type == 'static' and selected_tabs[0].id == 'guide'
 
202
 
 
203
    def __init__(self, tab_type, selected_tabs):
 
204
        Display.__init__(self)
 
205
        self.widget = selected_tabs[0].browser
 
206
 
 
207
class SiteDisplay(TabDisplay):
 
208
    _open_sites = {} # maps site ids -> BrowserNav objects for them
 
209
 
 
210
    @classmethod
 
211
    def on_sites_removed(cls, info_updater, id_list):
 
212
        for id in id_list:
 
213
            try:
 
214
                del cls._open_sites[id]
 
215
            except KeyError:
 
216
                pass
 
217
 
 
218
    @staticmethod
 
219
    def should_display(tab_type, selected_tabs):
 
220
        return tab_type == 'site' and len(selected_tabs) == 1
 
221
 
 
222
    def __init__(self, tab_type, selected_tabs):
 
223
        Display.__init__(self)
 
224
        guide_info = selected_tabs[0]
 
225
        if guide_info.id not in self._open_sites:
 
226
            self._open_sites[guide_info.id] = browser.BrowserNav(guide_info)
 
227
        self.widget = self._open_sites[guide_info.id]
 
228
 
 
229
class ItemListDisplayMixin(object):
 
230
    def on_selected(self):
 
231
        app.item_list_controller_manager.controller_created(self.controller)
 
232
        self.controller.start_tracking()
 
233
        self.restore_state()
 
234
 
 
235
    def on_activate(self):
 
236
        app.item_list_controller_manager.controller_displayed(self.controller)
 
237
        super(ItemListDisplayMixin, self).on_activate()
 
238
 
 
239
    def on_deactivate(self):
 
240
        app.item_list_controller_manager.controller_no_longer_displayed(
 
241
                self.controller)
 
242
 
 
243
    def cleanup(self):
 
244
        self.controller.stop_tracking()
 
245
        self.remember_state()
 
246
        app.item_list_controller_manager.controller_destroyed(self.controller)
 
247
 
 
248
    def remember_state(self):
 
249
        if self.controller.widget.in_list_view:
 
250
            app.frontend_states_memory.set_list_view(self.type, self.id)
 
251
        else:
 
252
            app.frontend_states_memory.set_std_view(self.type, self.id)
 
253
 
 
254
    def restore_state(self):
 
255
        if app.frontend_states_memory.query_list_view(self.type, self.id):
 
256
            self.widget.switch_to_list_view()
 
257
 
 
258
class ItemListDisplay(ItemListDisplayMixin, TabDisplay):
 
259
    def __init__(self, tab_type, selected_tabs):
 
260
        Display.__init__(self)
 
261
        tab = selected_tabs[0]
 
262
        self.controller = self.make_controller(tab)
 
263
        self.widget = self.controller.widget
 
264
        self.type = tab_type
 
265
        self.id = tab.id
 
266
 
 
267
    def make_controller(self, tab):
 
268
        raise NotImplementedError()
 
269
 
 
270
class FeedDisplay(ItemListDisplay):
 
271
    TAB_TYPE = 'feed'
 
272
    UPDATER_SIGNAL_NAME = 'feeds-changed'
 
273
 
 
274
    @classmethod
 
275
    def should_display(cls, tab_type, selected_tabs):
 
276
        return tab_type == cls.TAB_TYPE and len(selected_tabs) == 1
 
277
 
 
278
    def on_selected(self):
 
279
        ItemListDisplay.on_selected(self)
 
280
        self._signal_handler = app.info_updater.connect(
 
281
                self.UPDATER_SIGNAL_NAME, self._on_feeds_changed)
 
282
 
 
283
    def _on_feeds_changed(self, updater, info_list):
 
284
        for info in info_list:
 
285
            if info.id == self.id:
 
286
                self.controller.titlebar.update_title(info.name)
 
287
                return
 
288
 
 
289
    def cleanup(self):
 
290
        ItemListDisplay.cleanup(self)
 
291
        app.info_updater.disconnect(self._signal_handler)
 
292
        if widgetutil.feed_exists(self.feed_id):
 
293
            messages.MarkFeedSeen(self.feed_id).send_to_backend()
 
294
 
 
295
    def make_controller(self, tab):
 
296
        self.feed_id = tab.id
 
297
        return feedcontroller.FeedController(tab.id, tab.is_folder, tab.is_directory_feed)
 
298
 
 
299
class AudioFeedDisplay(FeedDisplay):
 
300
    TAB_TYPE = 'audio-feed'
 
301
    UPDATER_SIGNAL_NAME = 'audio-feeds-changed'
 
302
 
 
303
class PlaylistDisplay(ItemListDisplay):
 
304
    @staticmethod
 
305
    def should_display(tab_type, selected_tabs):
 
306
        return tab_type == 'playlist' and len(selected_tabs) == 1
 
307
 
 
308
    def on_selected(self):
 
309
        ItemListDisplay.on_selected(self)
 
310
        self._signal_handler = app.info_updater.connect('playlists-changed',
 
311
                self._on_playlists_changed)
 
312
 
 
313
    def _on_playlists_changed(self, updater, info_list):
 
314
        for info in info_list:
 
315
            if info.id == self.id:
 
316
                self.controller.titlebar.update_title(info.name)
 
317
                return
 
318
 
 
319
    def cleanup(self):
 
320
        ItemListDisplay.cleanup(self)
 
321
        app.info_updater.disconnect(self._signal_handler)
 
322
 
 
323
    def make_controller(self, playlist_info):
 
324
        return playlist.PlaylistView(playlist_info)
 
325
 
 
326
class SearchDisplay(ItemListDisplay):
 
327
    @staticmethod
 
328
    def should_display(tab_type, selected_tabs):
 
329
        return tab_type == 'static' and selected_tabs[0].id == 'search'
 
330
 
 
331
    def make_controller(self, tab):
 
332
        return itemlistcontroller.SearchController()
 
333
 
 
334
class AudioVideoItemsDisplay(ItemListDisplay):
 
335
    def remember_state(self):
 
336
        ItemListDisplay.remember_state(self)
 
337
        filters = self.widget.toolbar.active_filters()
 
338
        app.frontend_states_memory.set_filters(self.type, self.id, filters)
 
339
 
 
340
    def restore_state(self):
 
341
        initial_filters = app.frontend_states_memory.query_filters(self.type,
 
342
                self.id)
 
343
        if initial_filters:
 
344
            self.controller.set_item_filters(initial_filters)
 
345
        ItemListDisplay.restore_state(self)
 
346
 
 
347
class VideoItemsDisplay(AudioVideoItemsDisplay):
 
348
    @staticmethod
 
349
    def should_display(tab_type, selected_tabs):
 
350
        return tab_type == 'library' and selected_tabs[0].id == 'videos'
 
351
 
 
352
    def make_controller(self, tab):
 
353
        return itemlistcontroller.VideoItemsController()
 
354
 
 
355
class AudioItemsDisplay(AudioVideoItemsDisplay):
 
356
    @staticmethod
 
357
    def should_display(tab_type, selected_tabs):
 
358
        return tab_type == 'library' and selected_tabs[0].id == 'audios'
 
359
 
 
360
    def make_controller(self, tab):
 
361
        return itemlistcontroller.AudioItemsController()
 
362
 
 
363
class OtherItemsDisplay(ItemListDisplay):
 
364
    @staticmethod
 
365
    def should_display(tab_type, selected_tabs):
 
366
        return tab_type == 'library' and selected_tabs[0].id == 'others'
 
367
 
 
368
    def make_controller(self, tab):
 
369
        return itemlistcontroller.OtherItemsController()
 
370
 
 
371
class DownloadingDisplay(ItemListDisplay):
 
372
    @staticmethod
 
373
    def should_display(tab_type, selected_tabs):
 
374
        return tab_type == 'library' and selected_tabs[0].id == 'downloading'
 
375
 
 
376
    def make_controller(self, tab):
 
377
        return downloadscontroller.DownloadsController()
 
378
 
 
379
class VideoConversionsDisplay(TabDisplay):
 
380
    @staticmethod
 
381
    def should_display(tab_type, selected_tabs):
 
382
        return tab_type == 'library' and selected_tabs[0].id == 'conversions'
 
383
 
 
384
    def __init__(self, tab_type, selected_tabs):
 
385
        Display.__init__(self)
 
386
        self.controller = videoconversionscontroller.VideoConversionsController()
 
387
        self.widget = self.controller.widget
 
388
 
 
389
class FolderContentsDisplay(ItemListDisplayMixin, Display):
 
390
    def __init__(self, info):
 
391
        self.type = 'folder-contents'
 
392
        self.id = info.id
 
393
        self.controller = itemlistcontroller.FolderContentsController(info)
 
394
        self.widget = self.controller.widget
 
395
        Display.__init__(self)
 
396
 
 
397
class CantPlayWidget(widgetset.SolidBackground):
 
398
    def __init__(self):
 
399
        widgetset.SolidBackground.__init__(self, (0, 0, 0))
 
400
        vbox = widgetset.VBox()
 
401
        label = widgetset.Label(_(
 
402
            "%(appname)s can't play this file.  You may "
 
403
            "be able to open it with a different program",
 
404
            {"appname": config.get(prefs.SHORT_APP_NAME)}
 
405
            ))
 
406
        label.set_color((1, 1, 1))
 
407
        vbox.pack_start(label)
 
408
        table = widgetset.Table(2, 2)
 
409
        table.set_column_spacing(6)
 
410
        self.filename_label = self._make_label('')
 
411
        self.filetype_label  = self._make_label('')
 
412
        table.pack(widgetutil.align_left(self._make_heading(_('Filename:'))),
 
413
                0, 0)
 
414
        table.pack(widgetutil.align_left(self.filename_label), 1, 0)
 
415
        table.pack(widgetutil.align_left(self._make_heading(_('File type:'))),
 
416
                0, 1)
 
417
        table.pack(widgetutil.align_left(self.filetype_label), 1, 1)
 
418
        vbox.pack_start(widgetutil.align_left(table, top_pad=12))
 
419
        hbox = widgetset.HBox(spacing=12)
 
420
        reveal_button = widgetset.Button(_('Reveal File'))
 
421
        self.play_externally_button = widgetset.Button(_('Play Externally'))
 
422
        self.play_externally_button.connect('clicked', self._on_play_externally)
 
423
        skip_button = widgetset.Button(_('Skip'))
 
424
        reveal_button.connect('clicked', self._on_reveal)
 
425
        skip_button.connect('clicked', self._on_skip)
 
426
        hbox.pack_start(reveal_button)
 
427
        hbox.pack_start(self.play_externally_button)
 
428
        hbox.pack_start(skip_button)
 
429
        vbox.pack_start(widgetutil.align_center(hbox, top_pad=24))
 
430
        alignment = widgetset.Alignment(xalign=0.5, yalign=0.5)
 
431
        alignment.add(vbox)
 
432
        self.add(alignment)
 
433
 
 
434
    def _make_label(self, text):
 
435
        label = widgetset.Label(text)
 
436
        label.set_color((1, 1, 1))
 
437
        return label
 
438
 
 
439
    def _make_heading(self, text):
 
440
        label = self._make_label(text)
 
441
        label.set_bold(True)
 
442
        return label
 
443
 
 
444
    def _on_reveal(self, button):
 
445
        app.widgetapp.reveal_file(self.video_path)
 
446
 
 
447
    def _on_play_externally(self, button):
 
448
        app.widgetapp.open_file(self.video_path)
 
449
 
 
450
    def _on_skip(self, button):
 
451
        app.playback_manager.play_next_item(False)
 
452
 
 
453
    def set_video_path(self, video_path):
 
454
        self.video_path = video_path
 
455
        self.filename_label.set_text(os.path.split(video_path)[-1])
 
456
        self.filetype_label.set_text(os.path.splitext(video_path)[1])
 
457
        if filetypes.is_playable_filename(video_path):
 
458
            self.play_externally_button.set_text(_('Play Externally'))
 
459
        else:
 
460
            self.play_externally_button.set_text(_('Open Externally'))
 
461
 
 
462
class VideoDisplay(Display):
 
463
    def __init__(self, renderer):
 
464
        Display.__init__(self)
 
465
        self.create_signal('cant-play')
 
466
        self.create_signal('ready-to-play')
 
467
        self.renderer = renderer
 
468
        self.widget = widgetset.VBox()
 
469
        self.widget.pack_start(self.renderer, expand=True)
 
470
        self.cant_play_widget = CantPlayWidget()
 
471
        self._showing_renderer = True
 
472
        self.in_fullscreen = False
 
473
 
 
474
    def show_renderer(self):
 
475
        if not self._showing_renderer:
 
476
            self.widget.remove(self.cant_play_widget)
 
477
            self.widget.pack_start(self.renderer, expand=True)
 
478
            self._showing_renderer = True
 
479
 
 
480
    def show_play_external(self):
 
481
        if self._showing_renderer:
 
482
            self._prepare_remove_renderer()
 
483
            self.widget.remove(self.renderer)
 
484
            self.widget.pack_start(self.cant_play_widget, expand=True)
 
485
            self._showing_renderer = False
 
486
 
 
487
    def _open_success(self):
 
488
        self.emit('ready-to-play')
 
489
 
 
490
    def _open_error(self):
 
491
        messages.MarkItemWatched(self.item_info_id).send_to_backend()
 
492
        self.show_play_external()
 
493
        self.emit('cant-play')
 
494
 
 
495
    def setup(self, item_info, volume):
 
496
        self.show_renderer()
 
497
        self.cant_play_widget.set_video_path(item_info.video_path)
 
498
        self.item_info_id = item_info.id
 
499
        self.renderer.set_item(item_info, self._open_success, self._open_error)
 
500
        self.renderer.set_volume(volume)
 
501
 
 
502
    def enter_fullscreen(self):
 
503
        self.renderer.enter_fullscreen()
 
504
        self.in_fullscreen = True
 
505
 
 
506
    def exit_fullscreen(self):
 
507
        self.renderer.exit_fullscreen()
 
508
        self.in_fullscreen = False
 
509
 
 
510
    def prepare_switch_to_attached_playback(self):
 
511
        self.renderer.prepare_switch_to_attached_playback()
 
512
 
 
513
    def prepare_switch_to_detached_playback(self):
 
514
        self.renderer.prepare_switch_to_detached_playback()
 
515
 
 
516
    def _prepare_remove_renderer(self):
 
517
        if self.in_fullscreen:
 
518
            self.exit_fullscreen()
 
519
        if self.renderer:
 
520
            self.renderer.stop()
 
521
 
 
522
    def cleanup(self):
 
523
        if self._showing_renderer:
 
524
            self._prepare_remove_renderer()
 
525
        self.renderer.teardown()
 
526
        self.renderer = None
 
527
 
 
528
class MultipleSelectionDisplay(TabDisplay):
 
529
    @staticmethod
 
530
    def should_display(tab_type, selected_tabs):
 
531
        return len(selected_tabs) > 1
 
532
 
 
533
    def __init__(self, tab_type, selected_tabs):
 
534
        Display.__init__(self)
 
535
        self.type = tab_type
 
536
        self.child_count = self.folder_count = self.folder_child_count = 0
 
537
        if tab_type == 'feed':
 
538
            tab_list = app.tab_list_manager.feed_list
 
539
        elif tab_type == 'audio-feed':
 
540
            tab_list = app.tab_list_manager.audio_feed_list
 
541
        elif tab_type == 'site':
 
542
            tab_list = app.tab_list_manager.site_list
 
543
        else:
 
544
            tab_list = app.tab_list_manager.playlist_list
 
545
        for tab in selected_tabs:
 
546
            if hasattr(tab, "is_folder") and tab.is_folder:
 
547
                self.folder_count += 1
 
548
                self.folder_child_count += tab_list.get_child_count(tab.id)
 
549
            else:
 
550
                self.child_count += 1
 
551
        vbox = widgetset.VBox(spacing=20)
 
552
        label = self._make_label(tab_type, selected_tabs)
 
553
        label.set_size(2)
 
554
        label.set_color((0.3, 0.3, 0.3))
 
555
        vbox.pack_start(widgetutil.align_center(label))
 
556
        vbox.pack_start(widgetutil.align_center(
 
557
            self._make_buttons(tab_type)))
 
558
        self.widget = widgetutil.align_middle(vbox)
 
559
 
 
560
    def _make_label(self, tab_type, selected_tabs):
 
561
        label_parts = []
 
562
        # NOTE: we need to use ngettext because some languages have multiple
 
563
        # plural forms.
 
564
        if self.folder_count > 0:
 
565
            if tab_type in ('feed', 'audio-feed'):
 
566
                label_parts.append(ngettext(
 
567
                        '%(count)d Feed Folder Selected',
 
568
                        '%(count)d Feed Folders Selected',
 
569
                        self.folder_count,
 
570
                        {"count": self.folder_count}))
 
571
                label_parts.append(ngettext(
 
572
                        '(contains %(count)d feed)',
 
573
                        '(contains %(count)d feeds)',
 
574
                        self.folder_child_count,
 
575
                        {"count": self.folder_child_count}))
 
576
            else:
 
577
                label_parts.append(ngettext(
 
578
                        '%(count)d Playlist Folder Selected',
 
579
                        '%(count)d Playlist Folders Selected',
 
580
                        self.folder_count,
 
581
                        {"count": self.folder_count}))
 
582
                label_parts.append(ngettext(
 
583
                        '(contains %(count)d playlist)',
 
584
                        '(contains %(count)d playlist)',
 
585
                        self.folder_child_count,
 
586
                        {"count": self.folder_child_count}))
 
587
 
 
588
        if self.child_count > 0 and self.folder_count > 0:
 
589
            label_parts.append('')
 
590
        if self.child_count > 0:
 
591
            if tab_type in ('feed', 'audio-feed'):
 
592
                label_parts.append(ngettext(
 
593
                        '%(count)d Feed Selected',
 
594
                        '%(count)d Feeds Selected',
 
595
                        self.child_count,
 
596
                        {"count": self.child_count}))
 
597
            elif tab_type == "site":
 
598
                label_parts.append(ngettext(
 
599
                        '%(count)d Website Selected',
 
600
                        '%(count)d Websites Selected',
 
601
                        self.child_count,
 
602
                        {"count": self.child_count}))
 
603
            else:
 
604
                label_parts.append(ngettext(
 
605
                        '%(count)d Playlist Selected',
 
606
                        '%(count)d Playlists Selected',
 
607
                        self.child_count,
 
608
                        {"count": self.child_count}))
 
609
        return widgetset.Label('\n'.join(label_parts))
 
610
 
 
611
    def _make_buttons(self, tab_type):
 
612
        delete_button = widgetset.Button(_('Delete All'))
 
613
        delete_button.connect('clicked', self._on_delete_clicked)
 
614
        if self.folder_count > 0 or tab_type == "site":
 
615
            return delete_button
 
616
        create_folder_button = widgetset.Button(_('Put Into a New Folder'))
 
617
        create_folder_button.connect('clicked', self._on_new_folder_clicked)
 
618
        hbox = widgetset.HBox(spacing=12)
 
619
        hbox.pack_start(delete_button)
 
620
        hbox.pack_start(create_folder_button)
 
621
        return hbox
 
622
 
 
623
    def _on_delete_clicked(self, button):
 
624
        if self.type in ('feed', 'audio-feed'):
 
625
            app.widgetapp.remove_current_feed()
 
626
        elif self.type == 'site':
 
627
            app.widgetapp.remove_current_site()
 
628
        else:
 
629
            app.widgetapp.remove_current_playlist()
 
630
 
 
631
    def _on_new_folder_clicked(self, button):
 
632
        if self.type in ('feed', 'audio-feed'):
 
633
            section = {"feed": u"video", "audio-feed": u"audio"}
 
634
            app.widgetapp.add_new_feed_folder(add_selected=True,
 
635
                    default_type=self.type)
 
636
        else:
 
637
            app.widgetapp.add_new_playlist_folder(add_selected=True)
 
638
 
 
639
class DummyDisplay(TabDisplay):
 
640
    @staticmethod
 
641
    def should_display(tab_type, selected_tabs):
 
642
        return True
 
643
 
 
644
    def __init__(self, tab_type, selected_tabs):
 
645
        Display.__init__(self)
 
646
        text = '\n'.join(tab.name for tab in selected_tabs)
 
647
        label = widgetset.Label(text)
 
648
        label.set_size(3)
 
649
        label.set_bold(True)
 
650
        label.set_color((1.0, 0, 0))
 
651
        alignment = widgetset.Alignment(xalign=0.5, yalign=0.0)
 
652
        alignment.add(label)
 
653
        self.widget = alignment