예제 #1
0
class ListIndicatorTest(EntertainerTest):
    '''Test for entertainerlib.gui.widgets.list_indicator.'''

    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.indicator = ListIndicator(0.7, 0.8, 0.2, 0.045,
            ListIndicator.HORIZONTAL)

    def test_create(self):
        '''Test correct ListIndicator initialization.'''
        self.assertTrue(isinstance(self.indicator, ListIndicator))

    def test_currentmax(self):
        '''Test methods to handle the current and maximum attributes.'''
        self.indicator.set_current(10)
        self.assertEqual(self.indicator.get_current(), 1)
        self.indicator.set_current(-99)
        self.assertEqual(self.indicator.get_current(), 1)
        self.indicator.set_maximum(10)
        self.indicator.set_current(5)
        self.assertEqual(self.indicator.get_current(), 5)

    def test_setdelimiter(self):
        '''Test the set_delimiter method.'''
        self.indicator.set_maximum(100)
        self.indicator.set_current(50)
        self.indicator.set_delimiter(" * ")
        self.assertEqual(self.indicator.text.get_text(), "50 * 100")
예제 #2
0
class Album(Screen):
    '''Screen that allows user to browse and play tracks of the music album.'''

    def __init__(self, media_player, music_library, move_to_new_screen_callback,
        album):
        Screen.__init__(self, 'Album', move_to_new_screen_callback)

        self.media_player = media_player
        self.theme = self.config.theme
        self.library = music_library
        self.album = album
        self.art = None
        self.track_menu = None

        # Create and initialize screen items
        self.track_menu = self._create_track_menu()
        self.add(self.track_menu)
        self._create_album_cover_texture()
        self._create_album_info()

        self.screen_title = Label(0.13, "screentitle", 0, 0.87, "")
        self.screen_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.screen_title.width = 0.8
        self.add(self.screen_title)

        #List indicator
        self.li = ListIndicator(0.74, 0.85, 0.2, 0.045, ListIndicator.VERTICAL)
        self.li.set_maximum(len(self.album.tracks))
        self.add(self.li)

        self.track_menu.active = True
        self.track_menu.connect('selected', self._on_menu_selected)
        self.track_menu.connect('moved', self._display_selected_track)

    def _create_album_cover_texture(self):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        """
        if(self.album.has_album_art()):
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.album.album_art_url)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(
                self.theme.getImage("default_album_art"))
        self.art = EyeCandyTexture(0.1, 0.13, 0.3148, 0.5599, pixbuf)
        self.art.set_rotation(clutter.Y_AXIS, 25, 0, 0, 0)
        self.add(self.art)

    def _create_album_info(self):
        """
        Create album info labels.
        """
        if self.album.year != 0:
            album_text = self.album.title + ", " + str(self.album.year)
        else:
            album_text = self.album.title
        album = Label(0.0416, "text", 0.5, 0.13, album_text, font_weight="bold")
        album.set_ellipsize(pango.ELLIPSIZE_END)
        album.set_line_wrap(False)
        album.width = 0.45
        self.add(album)

        length = str(self.album.length / 60)
        num_of_tracks_text = _("%(total)s tracks, %(time)s minutes") % \
            {'total': len(self.album.tracks), 'time': length}
        num_of_tracks = Label(0.028, "subtitle", 0.5, 0.18,
            num_of_tracks_text, font_weight="bold")
        self.add(num_of_tracks)

    def _create_track_menu(self):
        """
        Create track menu. This menu contains list of all tracks on album.
        """
        menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)

        tracks = self.album.tracks
        tracks_list = [[track.title, track.length_string, track] \
            for track in tracks]
        menu.async_add(tracks_list)

        return menu

    def is_interested_in_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        return True

    def execute_play_action(self):
        """
        Override function from Screen class. See Screen class for
        better documentation.
        """
        track = self.track_menu.selected_userdata
        self.media_player.set_media(track)
        self.media_player.play()

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.track_menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.track_menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        track = self.track_menu.selected_userdata
        kwargs = { 'track' : track }
        self.callback("audio_play", kwargs)

    def _on_menu_selected(self, actor=None):
        '''Handle a *select command* if an item was selected.'''
        self._handle_select()

    def _display_selected_track(self, actor=None):
        '''Update of the list indicator and the screen's title'''
        self.li.set_current(self.track_menu.selected_index + 1)
        track = self.track_menu.selected_userdata
        self.screen_title.set_text(track.artist)
        self.screen_title.show()
예제 #3
0
class VideoClipsTab(Tab):
    """
    Tab can be used as part of the TabGroup

    Tab is a very simple container that contains all the widgets and logic
    of the tab page.
    """

    def __init__(self, media_player, video_library, move_to_new_screen_callback,
        name="clips", tab_title=_("Video clips")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
        self.media_player = media_player
        self.video_library = video_library
        self.theme = self.config.theme
        self.list_indicator = None
        self.clip_info = None
        self.menu = None
        self.clip_title = None

        if self.video_library.get_number_of_video_clips() == 0:
            self._create_empty_library_notice()
        else:
            # Start the loading animation while the menu is loading
            self.throbber = LoadingAnimation(0.7, 0.1)
            self.throbber.show()
            self.add(self.throbber)

            self.menu = self._create_menu()
            self.add(self.menu)
            self.menu.connect("moved", self._update_clip_info)
            self.menu.connect("selected", self._handle_select)
            self.menu.connect("activated", self._on_activated)
            self.menu.connect("filled", self._on_menu_filled)

            self.connect('activated', self._on_activated)
            self.connect('deactivated', self._on_deactivated)

    def can_activate(self):
        """
        Allow if we have some movies indexed.
        """
        if self.video_library.get_number_of_video_clips() == 0:
            return False
        else:
            return True

    def _create_empty_library_notice(self):
        """
        Create an information box that is displayed if there are no indexed
        movies.
        """
        message = _(
            "There are no indexed Video Clips in the Entertainer media "
            "library. Please add some folders containing video clips "
            "to the Library using the configuration tool.")
        Tab.show_empty_tab_notice(self, _("No video clips available!"), message)

    def _create_menu(self):
        """
        Create a view that is displayed when there are indexed clips in
        the video library.
        """
        menu = ImageMenu(0.04, 0.16, 0.23, self.y_for_x(0.23) * 0.7)
        menu.items_per_col = 2
        menu.visible_rows = 2
        menu.visible_cols = 4

        clips = self.video_library.get_video_clips()
        clips_list = [[clip.thumbnail_url, clip] for clip in clips]
        menu.async_add_clips(clips_list)

        # Create list indicator
        self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045,
            ListIndicator.HORIZONTAL)
        self.list_indicator.set_maximum(len(clips))
        self.list_indicator.show()
        self.add(self.list_indicator)

        # Create information labels
        self.clip_title = Label(0.042, "title", 0.15, 0.77, "",
            font_weight="bold")
        self.clip_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.clip_title.set_line_wrap(False)
        self.clip_title.width = 0.5
        self.add(self.clip_title)

        self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "")
        self.clip_info.set_ellipsize(pango.ELLIPSIZE_END)
        self.clip_info.set_line_wrap(False)
        self.clip_info.width = 0.5
        self.add(self.clip_info)

        return menu

    def _update_clip_info(self, event=None):
        '''Update the VideoClip information labels.'''
        if self.active:
            clip = self.menu.selected_userdata
            (folder, filename) = os.path.split(clip.filename)
            self.clip_title.set_text(filename)
            self.clip_info.set_text(folder)
            self.list_indicator.show()
            self.list_indicator.set_current(self.menu.selected_index + 1)
        else:
            self.clip_title.set_text("")
            self.clip_info.set_text("")
            self.list_indicator.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        clip = self.menu.selected_userdata
        self.media_player.set_media(clip)
        self.media_player.play()
        self.callback("video_osd")
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_clip_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_clip_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #4
0
class PhotoAlbums(Screen):
    '''Screen contains a list of photo albums and album previews.'''

    def __init__(self, image_library, move_to_new_screen_callback):
        Screen.__init__(self, 'PhotoAlbums', move_to_new_screen_callback)

        self.theme = self.config.theme
        self.image_library = image_library
        self.timeout = None # Timeout key (this is used when scrolling menu)

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Photographs"))
        self.add(screen_title)

        if self.image_library.get_number_of_albums() == 0:
            self._create_no_photos_information()
        else:
            # Album preview group
            self.preview = clutter.Group()
            self.preview.set_position(self.get_abs_x(0.07),
                self.get_abs_y(0.1953))
            self.preview.show()
            self.add(self.preview)

            self.preview_fade = None
            self.menu = None
            self.in_behaviour = None
            self.out_behaviour = None
            self.in_opacity = None
            self.out_opacity = None
            self.preview_textures = None
            self.menu = self._create_album_menu()
            self.add(self.menu)
            self.li = None
            self._create_list_indicator()

            self._update_album_preview(self.menu.selected_userdata)

            self.menu.connect('selected', self._handle_select)
            self.menu.connect('moved', self._display_selected_album)

    def _create_no_photos_information(self):
        """
        Create textures and labels for information screen. This is displayed
        instead of album list if there are no photos available and it helps
        users to add new photographs to the system.
        """
        # Create warning icon
        warning_icon = Texture(self.theme.getImage("warning_icon"), 0.28, 0.27)
        self.add(warning_icon)

        # Create warning title
        info_title = Label(0.0625, "title", 0.3367, 0.2709,
            _("No photographs available!"))
        self.add(info_title)

        # Create warning help text
        message = _(
            "There are no indexed photographs in the Entertainer media "
            "library. To add photographs, start the Content management tool "
            "and open the 'Images' tab. Now click on the 'Add' button and "
            "select some folders which contain image files.")
        info = Label(0.0417, "menuitem_inactive", 0.2804, 0.45, message)
        info.set_size(0.5, 0.5859)
        self.add(info)

    def _create_album_menu(self):
        """
        Create ImageAlbum-menu. This menu contains list of albums. It also
        displays number of photographs per album.
        """
        menu = TextMenu(0.5271, 0.3385, 0.4393, 0.0781)
        menu.visible_rows = 7

        albums = self.image_library.get_albums()
        albums_list = [[album.get_title(), str(album.get_number_of_images()),
            album] for album in albums if album.get_number_of_images() != 0]
        menu.async_add(albums_list)

        menu.active = True

        return menu

    def _create_album_preview(self, album):
        """
        Create a clutter.Group that contains album preview actors.
        """
        group = clutter.Group()
        group.set_position(self.get_abs_x(0.07), self.get_abs_y(0.1953))

        # Preview images
        images = album.get_preview_images(3)
        self.preview_textures = []

        max_w = 0.4026
        max_h = 0.5599
        abs_max_w = self.get_abs_x(max_w)
        abs_max_h = self.get_abs_y(max_h)

        for image in images:
            pix_buffer = gtk.gdk.pixbuf_new_from_file(image.get_thumbnail_url())
            ratio = float(pix_buffer.get_width())
            ratio /= float(pix_buffer.get_height())

            # Resize and center preview texture
            if ratio > 1:
                texture = EyeCandyTexture(0.0, 0.0, max_w, max_h / ratio,
                    pix_buffer)
                new_y = int((abs_max_h - abs_max_h / ratio) / 2.0)
                texture.set_position(0, new_y)
            else:
                texture = EyeCandyTexture(0.0, 0.0, max_w * ratio,
                    max_h, pix_buffer)
                new_x = int((abs_max_w - abs_max_w * ratio) / 2.0)
                texture.set_position(new_x, 0)

            texture.set_rotation(clutter.Y_AXIS, 25, 0, 0, 0)
            texture.set_opacity(0)

            self.preview_textures.append(texture)
            group.add(texture)
        self.preview_textures[0].set_opacity(255)

        title = Label(0.03646, "title", 0.4649, 0, album.get_title(),
            font_weight="bold")
        title.width = 0.4758
        title.set_ellipsize(pango.ELLIPSIZE_END)
        group.add(title)

        desc = Label(0.026, "subtitle", 0.4649, 0.0521, album.get_description())
        desc.width = 0.4758
        group.add(desc)

        return group

    def _update_album_preview(self, album):
        """
        Update album preview. Display preview images from the current album.
        @param album: Currently selected album in menu
        """
        if self.preview_fade is not None:
            gobject.source_remove(self.preview_fade)

        new = self._create_album_preview(album)

        if self.config.show_effects:
            old = self.preview
            new.set_opacity(0)
            self.preview = new
            self.add(self.preview)

            #Fade out timeline
            timeline1 = clutter.Timeline(500)
            alpha1 = clutter.Alpha(timeline1, clutter.EASE_IN_OUT_SINE)
            self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1)
            self.out_opacity.apply(old)

            timeline1.connect('completed', self._change_preview_timeline_ended,
                old)

            # Fade in timeline
            timeline2 = clutter.Timeline(500)
            alpha2 = clutter.Alpha(timeline2, clutter.EASE_IN_OUT_SINE)
            self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2)
            self.in_opacity.apply(new)

            # Start animation
            timeline1.start()
            timeline2.start()
        else:
            # Do it without animation
            if self.preview is not None:
                self.remove(self.preview)
            self.preview = new
            self.add(self.preview)

        if len(self.preview_textures) > 1:
            self.preview_fade = gobject.timeout_add(6000,
                self._change_preview_image)

        return False # see gobject.timeout_add() doc

    def _change_preview_timeline_ended(self, timeline, group):
        """
        This is a callback function for preview updates. This is called when
        transition effect is finished. This method removes old preview group
        from the stage.
        """
        self.remove(group)

    def _change_preview_image(self):
        """
        Run a timeline that crossfades preview images. This method is a callback
        that is called every 4 seconds.
        """
        if len(self.preview_textures)<=1:
            self.preview_textures[0].set_opacity(255)
        elif self.config.show_effects:
            #Fade out timeline
            fade_out = clutter.Timeline(500)
            alpha_out = clutter.Alpha(fade_out, clutter.EASE_IN_OUT_SINE)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
            self.out_behaviour.apply(self.preview_textures[0])

            # Fade in timeline
            fade_in = clutter.Timeline(500)
            alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
            self.in_behaviour.apply(self.preview_textures[1])

            # Start animation
            fade_out.start()
            fade_in.start()
        else:
            self.preview_textures[0].set_opacity(0)
            self.preview_textures[1].set_opacity(255)

        # Scroll images
        self.preview_textures = self.preview_textures[1:] + \
            self.preview_textures[:1]

        return True

    def _create_list_indicator(self):
        '''Create list indicator for albums list.'''
        self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
        albums = self.image_library.get_albums()
        albums_list = [[album.get_title(), str(album.get_number_of_images()),
            album] for album in albums if album.get_number_of_images() != 0]
        self.li.set_maximum(len(albums_list))
        self.add(self.li)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        album = self.menu.selected_userdata
        kwargs = { 'title' : album.get_title(), 'images' : album.get_images() }
        self.callback("photographs", kwargs)

    def handle_user_event(self, event):
        '''Handle screen specific user events unless the library is empty.'''
        if self.image_library.get_number_of_albums() == 0:
            return
        else:
            Screen.handle_user_event(self, event)

    def _display_selected_album(self, event=None):
        '''Update of the list indicator'''
        self.li.set_current(self.menu.selected_index + 1)
        # 500ms timeout before preview is updated (fast scrolling)
        if self.timeout is not None:
            gobject.source_remove(self.timeout)
        self.timeout = gobject.timeout_add(500, self._update_album_preview,
            self.menu.selected_userdata)
예제 #5
0
class TvEpisodes(Screen):
    '''Screen contains list of all episodes of one specific season.'''

    def __init__(self, media_player, move_to_new_screen_callback, episodes,
        tv_series):
        Screen.__init__(self, 'TvEpisodes', move_to_new_screen_callback)

        self.episodes = episodes
        self.media_player = media_player
        self.theme = self.config.theme
        self.tv_series = tv_series

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, self.tv_series.title)
        self.add(screen_title)

        self.scroll_area = None
        self.title = None
        self.thumb = None
        self.menu = self._create_episode_menu()
        self.add(self.menu)
        self._create_episode_info_box()

        #List indicator
        self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
        self.li.set_maximum(len(self.episodes))
        self.add(self.li)

        self.menu.connect("moved", self._update_episode_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_menu_activated)

    def _create_episode_menu(self):
        """Create a list of available seasons."""
        menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781)

        episodes_list = [[_("%(num)d. %(title)s") % \
            {'num': episode.number, 'title': episode.title},
            None, episode] for episode in self.episodes]
        menu.async_add(episodes_list)

        menu.active = True

        return menu

    def _create_thumbnail_texture(self):
        """Create a thumbnail texture. This is called as menu is scrolled."""
        if self.thumb:
            self.thumb.hide()

        # Thumbnail. Use cover art if thumbnail doesn't exist
        thumbnail = self.menu.selected_userdata.thumbnail_url
        if(thumbnail is not None):
            pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail)
            thumb_width = 0.2928
            thumb_height = 0.2799
            thumb_x = 0.05
            thumb_y = 0.2
        else:
            thumb_width = 0.1098
            thumb_height = 0.2799
            thumb_x = 0.20
            thumb_y = 0.15
            if(self.tv_series.has_cover_art()):
                pixbuf = gtk.gdk.pixbuf_new_from_file(
                    self.tv_series.cover_art_url)
            else:
                pixbuf = gtk.gdk.pixbuf_new_from_file(
                    self.theme.getImage("default_movie_art"))

        self.thumb = EyeCandyTexture(thumb_x, thumb_y, thumb_width,
            thumb_height, pixbuf)
        self.add(self.thumb)

    def _create_episode_info_box(self):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        """
        self._create_thumbnail_texture()

        # Title
        self.title = Label(0.04, "title", 0.05, 0.55,
            self.menu.selected_userdata.title, font_weight="bold")
        self.title.set_ellipsize(pango.ELLIPSIZE_END)
        self.title.set_line_wrap(False)
        self.title.width = 0.4
        self.add(self.title)

        # Plot
        plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot)
        plot.width = 0.4

        self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot)
        self.scroll_area.connect("activated", self._on_scroll_area_activated)
        self.add(self.scroll_area)

    def _update_episode_info(self, event=None):
        '''Update information from this episode.'''
        self.li.set_current(self.menu.selected_index + 1)

        self._create_thumbnail_texture()
        self.title.set_text(self.menu.selected_userdata.title)
        self.title.width = 0.4

        plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot)
        plot.width = 0.4
        self.scroll_area.set_content(plot)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        if self.menu.active:
            self.menu.up()
        else:
            self.scroll_area.scroll_up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        if self.menu.active:
            self.menu.down()
        else:
            self.scroll_area.scroll_down()

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        self.menu.active = False
        self.scroll_area.active = True

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        self.menu.active = True
        self.scroll_area.active = False

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        episode = self.menu.selected_userdata
        self.media_player.set_media(episode)
        self.media_player.play()
        self.callback("video_osd")

    def _on_menu_activated(self, event=None):
        '''Handle menu activation.'''
        self.scroll_area.active = False

    def _on_scroll_area_activated(self, event=None):
        '''Handle scroll_area activation.'''
        self.menu.active = False
예제 #6
0
class AlbumsTab(Tab):
    '''Tab to show album listings'''
    def __init__(self,
                 albums,
                 move_to_new_screen_callback,
                 name="albums",
                 tab_title=_("Albums")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)

        # Start the loading animation while the menu is loading
        self.throbber = LoadingAnimation(0.6, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        if len(albums) < 4:
            x_percent = 0.2928
            visible_rows = 1
            visible_cols = 3
        elif len(albums) < 13:
            x_percent = 0.1464
            visible_rows = 2
            visible_cols = 6
        else:
            x_percent = 0.1098
            visible_rows = 3
            visible_cols = 8

        # Create albums menu
        self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent))
        self.menu.visible_rows = visible_rows
        self.menu.visible_cols = visible_cols
        self.menu.items_per_col = self.menu.visible_rows
        self.add(self.menu)

        albums_list = [[album.album_art_url, album] for album in albums]
        self.menu.async_add_albums(albums_list)

        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
                                ListIndicator.HORIZONTAL)
        self.li.set_maximum(len(albums))
        self.li.show()
        self.add(self.li)

        # Create album information (displays current menuitem information)
        self.album_title = Label(0.045, "title", 0.22, 0.79, "")
        self.album_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.album_title.set_line_wrap(False)
        self.album_title.width = 0.366
        self.add(self.album_title)

        self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "")
        self.album_artist.set_ellipsize(pango.ELLIPSIZE_END)
        self.album_artist.set_line_wrap(False)
        self.album_artist.width = 0.366
        self.add(self.album_artist)

        self.album_tracks = Label(0.037, "subtitle", 0.22, 0.91, "")
        self.add(self.album_tracks)

        self.connect('activated', self._on_activated)
        self.connect('deactivated', self._on_deactivated)
        self.menu.connect("moved", self._update_album_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_activated)
        self.menu.connect("filled", self._on_menu_filled)

    def can_activate(self):
        '''Albums tab will always be created from an existing artist with at
        least one album.'''
        return True

    def _update_album_info(self, event=None):
        '''Update the album information labels.'''
        if self.active:
            album = self.menu.selected_userdata
            self.album_title.set_text(album.title)
            self.album_artist.set_text(album.artist)
            self.album_tracks.set_text(_("%(total)s tracks") % \
                {'total': len(album.tracks)})
            self.li.show()
            self.li.set_current(self.menu.selected_index + 1)
        else:
            self.album_title.set_text("")
            self.album_artist.set_text("")
            self.album_tracks.set_text("")
            self.li.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True  # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        album = self.menu.selected_userdata
        kwargs = {'album': album}
        self.callback("album", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_album_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_album_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #7
0
class MoviesTab(Tab):
    """
    Tab can be used as part of the TabGroup

    Tab is a very simple container that contains all the widgets and logic
    of the tab page.
    """
    def __init__(self,
                 video_library,
                 move_to_new_screen_callback,
                 name="movies",
                 tab_title=_("Movies")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)

        self.video_library = video_library
        self.theme = self.config.theme
        self.list_indicator = None
        self.movie_info = None
        self.menu = None
        self.movie_plot = None
        self.movie_title = None

        if self.video_library.get_number_of_movies() == 0:
            self._create_empty_library_notice()
        else:
            # Start the loading animation while the menu is loading
            self.throbber = LoadingAnimation(0.1, 0.1)
            self.throbber.show()
            self.add(self.throbber)

            self.menu = self._create_menu()
            self.add(self.menu)
            self.menu.connect("moved", self._update_movie_info)
            self.menu.connect("selected", self._handle_select)
            self.menu.connect("activated", self._on_activated)
            self.menu.connect("filled", self._on_menu_filled)

            self.connect('activated', self._on_activated)
            self.connect('deactivated', self._on_deactivated)

    def can_activate(self):
        """
        Allow if we have some movies indexed.
        """
        if self.video_library.get_number_of_movies() == 0:
            return False
        else:
            return True

    def _create_empty_library_notice(self):
        """
        Create an information box that is displayed if there are no indexed
        movies.
        """
        message = _(
            "There are no indexed movies in Entertainer media library.  To "
            "add movies, click on 'content' button on toolbar and open "
            "'videos' tab. Now click on 'add' button and select some folders "
            "which contains movie files.")
        Tab.show_empty_tab_notice(self, _("No movies available!"), message)

    def _create_menu(self):
        """
        Create a view that is displayed when there is indexed movies in
        the video library.
        """
        #Create movie menu
        menu = ImageMenu(0.06, 0.18, 0.12, 0.25)
        menu.items_per_col = 2
        menu.visible_rows = 2
        menu.visible_cols = 7

        movies = self.video_library.get_movies()
        movies_list = [[movie.cover_art_url, movie] for movie in movies]
        menu.async_add_videos(movies_list)

        # Create list indicator
        self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,
                                            ListIndicator.HORIZONTAL)
        self.list_indicator.set_maximum(len(movies))
        self.show()
        self.add(self.list_indicator)

        # Create information labels
        self.movie_title = Label(0.042,
                                 "title",
                                 0.2,
                                 0.75,
                                 "",
                                 font_weight="bold")
        self.movie_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.movie_title.set_line_wrap(False)
        self.movie_title.width = 0.5
        self.add(self.movie_title)

        self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "")
        self.movie_info.set_ellipsize(pango.ELLIPSIZE_END)
        self.movie_info.set_line_wrap(False)
        self.movie_info.width = 0.5
        self.add(self.movie_info)

        self.movie_plot = Label(0.025, "subtitle", 0.2, 0.85, "")
        self.movie_plot.width = 0.7
        self.add(self.movie_plot)

        return menu

    def _update_movie_info(self, event=None):
        '''Update the movie information labels.'''
        if self.active:
            movie = self.menu.selected_userdata
            genres = movie.genres
            if len(genres) > 1:
                genre = genres[0] + "/" + genres[1]
            else:
                genre = genres[0]

            self.movie_title.set_text(_("%(title)s (%(year)s)") % \
                {'title': movie.title, 'year': movie.year})
            self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \
                {'min': movie.runtime, 'genre': genre})
            self.movie_plot.set_text(movie.short_plot)
            self.list_indicator.show()
            self.list_indicator.set_current(self.menu.selected_index + 1)
        else:
            self.movie_title.set_text("")
            self.movie_info.set_text("")
            self.movie_plot.set_text("")
            self.list_indicator.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True  # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        movie = self.menu.selected_userdata
        kwargs = {'movie': movie}
        self.callback("movie", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_movie_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_movie_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #8
0
class MoviesTab(Tab):
    """
    Tab can be used as part of the TabGroup

    Tab is a very simple container that contains all the widgets and logic
    of the tab page.
    """

    def __init__(self, video_library, move_to_new_screen_callback,
        name="movies", tab_title=_("Movies")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)

        self.video_library = video_library
        self.theme = self.config.theme
        self.list_indicator = None
        self.movie_info = None
        self.menu = None
        self.movie_plot = None
        self.movie_title = None

        if self.video_library.get_number_of_movies() == 0:
            self._create_empty_library_notice()
        else:
            # Start the loading animation while the menu is loading
            self.throbber = LoadingAnimation(0.1, 0.1)
            self.throbber.show()
            self.add(self.throbber)

            self.menu = self._create_menu()
            self.add(self.menu)
            self.menu.connect("moved", self._update_movie_info)
            self.menu.connect("selected", self._handle_select)
            self.menu.connect("activated", self._on_activated)
            self.menu.connect("filled", self._on_menu_filled)

            self.connect('activated', self._on_activated)
            self.connect('deactivated', self._on_deactivated)

    def can_activate(self):
        """
        Allow if we have some movies indexed.
        """
        if self.video_library.get_number_of_movies() == 0:
            return False
        else:
            return True

    def _create_empty_library_notice(self):
        """
        Create an information box that is displayed if there are no indexed
        movies.
        """
        message = _(
            "There are no indexed movies in Entertainer media library.  To "
            "add movies, click on 'content' button on toolbar and open "
            "'videos' tab. Now click on 'add' button and select some folders "
            "which contains movie files.")
        Tab.show_empty_tab_notice(self, _("No movies available!"), message)

    def _create_menu(self):
        """
        Create a view that is displayed when there is indexed movies in
        the video library.
        """
        #Create movie menu
        menu = ImageMenu(0.06, 0.18, 0.12, 0.25)
        menu.items_per_col = 2
        menu.visible_rows = 2
        menu.visible_cols = 7

        movies = self.video_library.get_movies()
        movies_list = [[movie.cover_art_url, movie] for movie in movies]
        menu.async_add_videos(movies_list)

        # Create list indicator
        self.list_indicator = ListIndicator(0.75, 0.76, 0.2, 0.045,
            ListIndicator.HORIZONTAL)
        self.list_indicator.set_maximum(len(movies))
        self.show()
        self.add(self.list_indicator)

        # Create information labels
        self.movie_title = Label(0.042, "title", 0.2, 0.75, "",
            font_weight="bold")
        self.movie_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.movie_title.set_line_wrap(False)
        self.movie_title.width = 0.5
        self.add(self.movie_title)

        self.movie_info = Label(0.034, "subtitle", 0.2, 0.8, "")
        self.movie_info.set_ellipsize(pango.ELLIPSIZE_END)
        self.movie_info.set_line_wrap(False)
        self.movie_info.width = 0.5
        self.add(self.movie_info)

        self.movie_plot = Label(0.025, "subtitle", 0.2, 0.85, "")
        self.movie_plot.width = 0.7
        self.add(self.movie_plot)

        return menu

    def _update_movie_info(self, event=None):
        '''Update the movie information labels.'''
        if self.active:
            movie = self.menu.selected_userdata
            genres = movie.genres
            if len(genres) > 1:
                genre = genres[0] + "/" + genres[1]
            else:
                genre = genres[0]

            self.movie_title.set_text(_("%(title)s (%(year)s)") % \
                {'title': movie.title, 'year': movie.year})
            self.movie_info.set_text(_("%(min)d min, (%(genre)s)") % \
                {'min': movie.runtime, 'genre': genre})
            self.movie_plot.set_text(movie.short_plot)
            self.list_indicator.show()
            self.list_indicator.set_current(self.menu.selected_index + 1)
        else:
            self.movie_title.set_text("")
            self.movie_info.set_text("")
            self.movie_plot.set_text("")
            self.list_indicator.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        movie = self.menu.selected_userdata
        kwargs = { 'movie' : movie }
        self.callback("movie", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_movie_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_movie_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #9
0
class TvEpisodes(Screen):
    '''Screen contains list of all episodes of one specific season.'''
    def __init__(self, media_player, move_to_new_screen_callback, episodes,
                 tv_series):
        Screen.__init__(self, 'TvEpisodes', move_to_new_screen_callback)

        self.episodes = episodes
        self.media_player = media_player
        self.theme = self.config.theme
        self.tv_series = tv_series

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87,
                             self.tv_series.title)
        self.add(screen_title)

        self.scroll_area = None
        self.title = None
        self.thumb = None
        self.menu = self._create_episode_menu()
        self.add(self.menu)
        self._create_episode_info_box()

        #List indicator
        self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
        self.li.set_maximum(len(self.episodes))
        self.add(self.li)

        self.menu.connect("moved", self._update_episode_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_menu_activated)

    def _create_episode_menu(self):
        """Create a list of available seasons."""
        menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781)

        episodes_list = [[_("%(num)d. %(title)s") % \
            {'num': episode.number, 'title': episode.title},
            None, episode] for episode in self.episodes]
        menu.async_add(episodes_list)

        menu.active = True

        return menu

    def _create_thumbnail_texture(self):
        """Create a thumbnail texture. This is called as menu is scrolled."""
        if self.thumb:
            self.thumb.hide()

        # Thumbnail. Use cover art if thumbnail doesn't exist
        thumbnail = self.menu.selected_userdata.thumbnail_url
        if (thumbnail is not None):
            pixbuf = gtk.gdk.pixbuf_new_from_file(thumbnail)
            thumb_width = 0.2928
            thumb_height = 0.2799
            thumb_x = 0.05
            thumb_y = 0.2
        else:
            thumb_width = 0.1098
            thumb_height = 0.2799
            thumb_x = 0.20
            thumb_y = 0.15
            if (self.tv_series.has_cover_art()):
                pixbuf = gtk.gdk.pixbuf_new_from_file(
                    self.tv_series.cover_art_url)
            else:
                pixbuf = gtk.gdk.pixbuf_new_from_file(
                    self.theme.getImage("default_movie_art"))

        self.thumb = EyeCandyTexture(thumb_x, thumb_y, thumb_width,
                                     thumb_height, pixbuf)
        self.add(self.thumb)

    def _create_episode_info_box(self):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        """
        self._create_thumbnail_texture()

        # Title
        self.title = Label(0.04,
                           "title",
                           0.05,
                           0.55,
                           self.menu.selected_userdata.title,
                           font_weight="bold")
        self.title.set_ellipsize(pango.ELLIPSIZE_END)
        self.title.set_line_wrap(False)
        self.title.width = 0.4
        self.add(self.title)

        # Plot
        plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot)
        plot.width = 0.4

        self.scroll_area = ScrollArea(0.05, 0.63, 0.4, 0.15, plot)
        self.scroll_area.connect("activated", self._on_scroll_area_activated)
        self.add(self.scroll_area)

    def _update_episode_info(self, event=None):
        '''Update information from this episode.'''
        self.li.set_current(self.menu.selected_index + 1)

        self._create_thumbnail_texture()
        self.title.set_text(self.menu.selected_userdata.title)
        self.title.width = 0.4

        plot = Label(0.029, "subtitle", 0, 0, self.menu.selected_userdata.plot)
        plot.width = 0.4
        self.scroll_area.set_content(plot)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        if self.menu.active:
            self.menu.up()
        else:
            self.scroll_area.scroll_up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        if self.menu.active:
            self.menu.down()
        else:
            self.scroll_area.scroll_down()

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        self.menu.active = False
        self.scroll_area.active = True

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        self.menu.active = True
        self.scroll_area.active = False

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        episode = self.menu.selected_userdata
        self.media_player.set_media(episode)
        self.media_player.play()
        self.callback("video_osd")

    def _on_menu_activated(self, event=None):
        '''Handle menu activation.'''
        self.scroll_area.active = False

    def _on_scroll_area_activated(self, event=None):
        '''Handle scroll_area activation.'''
        self.menu.active = False
예제 #10
0
class TracksTab(Tab):
    '''Tab for the artist screen to show track listings'''

    def __init__(self, tracks, move_to_new_screen_callback, name="tracks",
        tab_title=_("Tracks")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)

        # Start the loading animation while the menu is loading
        self.throbber = LoadingAnimation(0.1, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        self.menu = TextMenu(0.0586, 0.2083, 0.2928, 0.0781)
        self.menu.items_per_row = 3
        self.menu.visible_rows = 7
        self.menu.visible_cols = 3
        self.menu.active = False
        self.menu.cursor = None
        self.add(self.menu)

        tracks_list = [[track.title, None, track] for track in tracks]
        self.menu.async_add_artists(tracks_list)

        self.track_title = Label(0.045, "title", 0.22, 0.79, "")
        self.track_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.track_title.set_line_wrap(False)
        self.track_title.width = 0.366
        self.add(self.track_title)

        self.track_number = Label(0.037, "subtitle", 0.22, 0.86, "")
        self.track_number.set_ellipsize(pango.ELLIPSIZE_END)
        self.track_number.set_line_wrap(False)
        self.track_number.width = 0.366
        self.add(self.track_number)

        self.track_length = Label(0.037, "subtitle", 0.22, 0.91, "")
        self.add(self.track_length)

        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
            ListIndicator.VERTICAL)
        self.li.set_maximum(len(tracks))
        self.li.show()
        self.add(self.li)

        self.connect('activated', self._on_activated)
        self.connect('deactivated', self._on_deactivated)
        self.menu.connect("moved", self._update_track_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_activated)
        self.menu.connect("filled", self._on_menu_filled)

    def can_activate(self):
        '''Tracks tab will always be created from an existing artist with at
        least one track.'''
        return True

    def _update_track_info(self, event=None):
        '''Update the track information labels'''
        if self.active:
            track = self.menu.selected_userdata
            self.track_title.set_text(track.title)
            self.track_length.set_text(track.length_string)
            self.track_number.set_text(_("Track %(track)d from %(album)s") % \
                {'track': track.number, 'album': track.album.title})
            self.li.show()
            self.li.set_current(self.menu.selected_index + 1)
        else:
            self.track_title.set_text("")
            self.track_length.set_text("")
            self.track_number.set_text("")
            self.li.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        track = self.menu.selected_userdata
        kwargs = { 'track' : track }
        self.callback("audio_play", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_track_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_track_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #11
0
class TvSeries(Screen):
    '''Screen that contains all seasons of one TV series.'''
    def __init__(self, video_library, move_to_new_screen_callback, tv_series):
        Screen.__init__(self, 'TvSeries', move_to_new_screen_callback)

        self.theme = self.config.theme
        self.tv_series = tv_series
        self.video_library = video_library

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87,
                             self.tv_series.title)
        self.add(screen_title)

        self.art = None
        self.menu = None
        self._create_series_cover_texture()
        self.menu = self._create_season_menu()
        self.add(self.menu)

        #List indicator
        self.li = ListIndicator(0.8, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
        self.li.set_maximum(len(self.tv_series.seasons))
        self.add(self.li)

        self.menu.connect("moved", self._update_season_info)
        self.menu.connect("selected", self._handle_select)

    def _create_season_menu(self):
        """
        Create a list of available seasons
        """
        menu = TextMenu(0.4978, 0.1563, 0.4393, 0.0781)

        seasons = self.tv_series.seasons
        seasons.sort()

        seasons_list = [[_("Season %(num)s") % {'num': season}, None, season] \
            for season in seasons]
        menu.async_add(seasons_list)

        menu.active = True

        return menu

    def _create_series_cover_texture(self):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        """
        if (self.tv_series.has_cover_art()):
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.tv_series.cover_art_url)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(
                self.theme.getImage("default_movie_art"))
        self.art = EyeCandyTexture(0.16, 0.15, 0.2196, 0.5859, pixbuf)
        self.add(self.art)

    def _update_season_info(self, event=None):
        '''Update the ListIndicator.'''
        self.li.set_current(self.menu.selected_index + 1)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        season = self.menu.selected_userdata
        episodes = self.video_library.get_episodes_from_season(
            self.tv_series.title, season)
        kwargs = {'episodes': episodes, 'tv_series': self.tv_series}
        self.callback("tv_episodes", kwargs)
예제 #12
0
class PhotoAlbums(Screen):
    '''Screen contains a list of photo albums and album previews.'''
    def __init__(self, image_library, move_to_new_screen_callback):
        Screen.__init__(self, 'PhotoAlbums', move_to_new_screen_callback)

        self.theme = self.config.theme
        self.image_library = image_library
        self.timeout = None  # Timeout key (this is used when scrolling menu)

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Photographs"))
        self.add(screen_title)

        if self.image_library.get_number_of_albums() == 0:
            self._create_no_photos_information()
        else:
            # Album preview group
            self.preview = clutter.Group()
            self.preview.set_position(self.get_abs_x(0.07),
                                      self.get_abs_y(0.1953))
            self.preview.show()
            self.add(self.preview)

            self.preview_fade = None
            self.menu = None
            self.in_behaviour = None
            self.out_behaviour = None
            self.in_opacity = None
            self.out_opacity = None
            self.preview_textures = None
            self.menu = self._create_album_menu()
            self.add(self.menu)
            self.li = None
            self._create_list_indicator()

            self._update_album_preview(self.menu.selected_userdata)

            self.menu.connect('selected', self._handle_select)
            self.menu.connect('moved', self._display_selected_album)

    def _create_no_photos_information(self):
        """
        Create textures and labels for information screen. This is displayed
        instead of album list if there are no photos available and it helps
        users to add new photographs to the system.
        """
        # Create warning icon
        warning_icon = Texture(self.theme.getImage("warning_icon"), 0.28, 0.27)
        self.add(warning_icon)

        # Create warning title
        info_title = Label(0.0625, "title", 0.3367, 0.2709,
                           _("No photographs available!"))
        self.add(info_title)

        # Create warning help text
        message = _(
            "There are no indexed photographs in the Entertainer media "
            "library. To add photographs, start the Content management tool "
            "and open the 'Images' tab. Now click on the 'Add' button and "
            "select some folders which contain image files.")
        info = Label(0.0417, "menuitem_inactive", 0.2804, 0.45, message)
        info.set_size(0.5, 0.5859)
        self.add(info)

    def _create_album_menu(self):
        """
        Create ImageAlbum-menu. This menu contains list of albums. It also
        displays number of photographs per album.
        """
        menu = TextMenu(0.5271, 0.3385, 0.4393, 0.0781)
        menu.visible_rows = 7

        albums = self.image_library.get_albums()
        albums_list = [[
            album.get_title(),
            str(album.get_number_of_images()), album
        ] for album in albums if album.get_number_of_images() != 0]
        menu.async_add(albums_list)

        menu.active = True

        return menu

    def _create_album_preview(self, album):
        """
        Create a clutter.Group that contains album preview actors.
        """
        group = clutter.Group()
        group.set_position(self.get_abs_x(0.07), self.get_abs_y(0.1953))

        # Preview images
        images = album.get_preview_images(3)
        self.preview_textures = []

        max_w = 0.4026
        max_h = 0.5599
        abs_max_w = self.get_abs_x(max_w)
        abs_max_h = self.get_abs_y(max_h)

        for image in images:
            pix_buffer = gtk.gdk.pixbuf_new_from_file(
                image.get_thumbnail_url())
            ratio = float(pix_buffer.get_width())
            ratio /= float(pix_buffer.get_height())

            # Resize and center preview texture
            if ratio > 1:
                texture = EyeCandyTexture(0.0, 0.0, max_w, max_h / ratio,
                                          pix_buffer)
                new_y = int((abs_max_h - abs_max_h / ratio) / 2.0)
                texture.set_position(0, new_y)
            else:
                texture = EyeCandyTexture(0.0, 0.0, max_w * ratio, max_h,
                                          pix_buffer)
                new_x = int((abs_max_w - abs_max_w * ratio) / 2.0)
                texture.set_position(new_x, 0)

            texture.set_rotation(clutter.Y_AXIS, 25, 0, 0, 0)
            texture.set_opacity(0)

            self.preview_textures.append(texture)
            group.add(texture)
        self.preview_textures[0].set_opacity(255)

        title = Label(0.03646,
                      "title",
                      0.4649,
                      0,
                      album.get_title(),
                      font_weight="bold")
        title.width = 0.4758
        title.set_ellipsize(pango.ELLIPSIZE_END)
        group.add(title)

        desc = Label(0.026, "subtitle", 0.4649, 0.0521,
                     album.get_description())
        desc.width = 0.4758
        group.add(desc)

        return group

    def _update_album_preview(self, album):
        """
        Update album preview. Display preview images from the current album.
        @param album: Currently selected album in menu
        """
        if self.preview_fade is not None:
            gobject.source_remove(self.preview_fade)

        new = self._create_album_preview(album)

        if self.config.show_effects:
            old = self.preview
            new.set_opacity(0)
            self.preview = new
            self.add(self.preview)

            #Fade out timeline
            timeline1 = clutter.Timeline(500)
            alpha1 = clutter.Alpha(timeline1, clutter.EASE_IN_OUT_SINE)
            self.out_opacity = clutter.BehaviourOpacity(255, 0, alpha1)
            self.out_opacity.apply(old)

            timeline1.connect('completed', self._change_preview_timeline_ended,
                              old)

            # Fade in timeline
            timeline2 = clutter.Timeline(500)
            alpha2 = clutter.Alpha(timeline2, clutter.EASE_IN_OUT_SINE)
            self.in_opacity = clutter.BehaviourOpacity(0, 255, alpha2)
            self.in_opacity.apply(new)

            # Start animation
            timeline1.start()
            timeline2.start()
        else:
            # Do it without animation
            if self.preview is not None:
                self.remove(self.preview)
            self.preview = new
            self.add(self.preview)

        if len(self.preview_textures) > 1:
            self.preview_fade = gobject.timeout_add(6000,
                                                    self._change_preview_image)

        return False  # see gobject.timeout_add() doc

    def _change_preview_timeline_ended(self, timeline, group):
        """
        This is a callback function for preview updates. This is called when
        transition effect is finished. This method removes old preview group
        from the stage.
        """
        self.remove(group)

    def _change_preview_image(self):
        """
        Run a timeline that crossfades preview images. This method is a callback
        that is called every 4 seconds.
        """
        if len(self.preview_textures) <= 1:
            self.preview_textures[0].set_opacity(255)
        elif self.config.show_effects:
            #Fade out timeline
            fade_out = clutter.Timeline(500)
            alpha_out = clutter.Alpha(fade_out, clutter.EASE_IN_OUT_SINE)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
            self.out_behaviour.apply(self.preview_textures[0])

            # Fade in timeline
            fade_in = clutter.Timeline(500)
            alpha_in = clutter.Alpha(fade_in, clutter.EASE_IN_OUT_SINE)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
            self.in_behaviour.apply(self.preview_textures[1])

            # Start animation
            fade_out.start()
            fade_in.start()
        else:
            self.preview_textures[0].set_opacity(0)
            self.preview_textures[1].set_opacity(255)

        # Scroll images
        self.preview_textures = self.preview_textures[1:] + \
            self.preview_textures[:1]

        return True

    def _create_list_indicator(self):
        '''Create list indicator for albums list.'''
        self.li = ListIndicator(0.77, 0.9, 0.2, 0.045, ListIndicator.VERTICAL)
        albums = self.image_library.get_albums()
        albums_list = [[
            album.get_title(),
            str(album.get_number_of_images()), album
        ] for album in albums if album.get_number_of_images() != 0]
        self.li.set_maximum(len(albums_list))
        self.add(self.li)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        album = self.menu.selected_userdata
        kwargs = {'title': album.get_title(), 'images': album.get_images()}
        self.callback("photographs", kwargs)

    def handle_user_event(self, event):
        '''Handle screen specific user events unless the library is empty.'''
        if self.image_library.get_number_of_albums() == 0:
            return
        else:
            Screen.handle_user_event(self, event)

    def _display_selected_album(self, event=None):
        '''Update of the list indicator'''
        self.li.set_current(self.menu.selected_index + 1)
        # 500ms timeout before preview is updated (fast scrolling)
        if self.timeout is not None:
            gobject.source_remove(self.timeout)
        self.timeout = gobject.timeout_add(500, self._update_album_preview,
                                           self.menu.selected_userdata)
예제 #13
0
class ScrollArea(Base, clutter.Group):
    """Wrapper of a clutter Group that allows for scrolling. ScrollArea
    modifies the width of the content and it assumes that the content uses
    percent modification (read: not default clutter objects)."""
    __gsignals__ = {
        'activated' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
        'moving' : ( gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, () ),
        }

    MODE_SELECTION = 0
    MODE_MOTION = 1
    MODE_STOP = 2
    STEP_SIZE_PERCENT = 0.04

    def __init__(self, x, y, width, height, content):
        Base.__init__(self)
        clutter.Group.__init__(self)

        self._motion_buffer = MotionBuffer()
        self._offset = 0        # Drives the content motion.
        self._offset_max = 0    # Maximum value of offset (equal on bottom).
        self._old_offset = 0    # Stores the old value of offset on motions.
        self._motion_handler = 0
        self._active = None

        self.step_size = self.get_abs_y(self.STEP_SIZE_PERCENT)

        # Allowed area for the widget's scrolling content.
        self.area_width = self.get_abs_x(width)
        self.area_height = self.get_abs_y(height)

        # Create content position indicator
        self.indicator = ListIndicator(3 * width / 4, height, 0.2, 0.045,
            ListIndicator.VERTICAL)
        self.indicator.hide_position()
        self.indicator.set_maximum(2)
        self.add(self.indicator)

        # A clipped Group to receive the content.
        self._fixed_group = clutter.Group()
        self._fixed_group.set_clip(0, 0, self.area_width, self.area_height)
        self.add(self._fixed_group)
        self.content = None

        self._motion_timeline = clutter.Timeline(500)
        self._motion_timeline.connect('completed',
            self._motion_timeline_callback, None)
        self._motion_alpha = clutter.Alpha(self._motion_timeline,
            clutter.EASE_OUT_SINE)
        self._motion_behaviour = LoopedPathBehaviour(self._motion_alpha)

        self.set_content(content)

        self.active = None

        # Preparation to pointer events handling.
        self.set_reactive(True)
        self.connect('button-press-event', self._on_button_press_event)
        self.connect('button-release-event', self._on_button_release_event)
        self.connect('scroll-event', self._on_scroll_event)

        self.set_position(self.get_abs_x(x), self.get_abs_y(y))

    @property
    def on_top(self):
        """True if we're on top."""
        return self._offset == 0

    @property
    def on_bottom(self):
        """True if we're on bottom."""
        return self._offset == self._offset_max

    def _get_active(self):
        """Active property getter."""
        return self._active

    def _set_active(self, boolean):
        """Active property setter."""
        if self._active == boolean:
            return

        self._active = boolean
        if boolean:
            # Show indicator if there is need for scrolling.
            if self._offset_max >= 0:
                self.indicator.show()

            self.set_opacity(255)
            self.emit('activated')
        else:
            self.indicator.hide()
            self.set_opacity(128)

    active = property(_get_active, _set_active)

    def _get_offset(self):
        """Get current offset value."""
        return self._offset

    def _set_offset(self, integer):
        """Set current offset value."""
        if self._offset == integer:
            return

        self._offset = integer

        if self._offset < 0:
            self._offset = 0
        elif self._offset > self._offset_max:
            self._offset = self._offset_max

        self.content.set_position(0, - self._offset)

        # Indicator updates.
        if self.on_top:
            self.indicator.set_current(1)
        elif self.on_bottom:
            self.indicator.set_current(2)

    offset = property(_get_offset, _set_offset)

    def set_content(self, content):
        """Set content into scroll area."""
        if self.content is not None:
            self._fixed_group.remove(self.content)
            self._motion_behaviour.remove(self.content)

        self.content = content
        self._fixed_group.add(content)

        self._offset_max = self.content.get_height() - self.area_height

        self._motion_behaviour.apply(self.content)

    def stop_animation(self):
        """Stops the timeline driving animation."""
        self._motion_timeline.stop()

    def scroll_to_top(self):
        """Scroll content back to top."""
        self.offset = 0

    def scroll_to_bottom(self):
        """Scroll content as much as possible."""
        self.offset = self._offset_max

    def scroll_up(self):
        """Scroll up by one step size."""
        self.offset -= self.step_size

    def scroll_down(self):
        """Scroll down by one step size."""
        self.offset += self.step_size

    def scroll_page_up(self):
        """Scroll up by one page. Page is a scroll area height."""
        self.offset -= self.area_height

    def scroll_page_down(self):
        self.offset += self.area_height

    def _update_motion_behaviour(self, target):
        """Preparation of looped behaviour applied to the content."""
        self._motion_behaviour.start_knot = (0.0, -self.offset)
        self._motion_behaviour.end_knot = (0.0, -target)
        self._motion_behaviour.start_index = 0.0
        # Need to set the end index to 0.9999. Indeed the LoopedPathBehaviour
        # uses an index in [0, 1[. So index = 1 is equivalent to index = 0, the
        # Actor will the be placed on the start_knot.
        self._motion_behaviour.end_index = 0.9999

    def _on_button_press_event(self, actor, event):
        """button-press-event handler."""
        clutter.grab_pointer(self)
        if not self.handler_is_connected(self._motion_handler):
            self._motion_handler = self.connect('motion-event',
                self._on_motion_event)

        if self._motion_timeline.is_playing():
            # A click with an animation pending should stop the animation.
            self._motion_timeline.stop()

            # Go to MODE_STOP to handle correctly next button-release event.
            self._event_mode = self.MODE_STOP
            self.offset = -self.content.get_y()
        else:
            # No animation pending so we're going to do nothing or to move
            # all the content.
            self._old_offset = self.offset
            self._motion_buffer.start(event)
            self._event_mode = self.MODE_SELECTION

        return False

    def _on_button_release_event(self, actor, event):
        """button-release-event handler."""
        clutter.ungrab_pointer()
        if self.handler_is_connected(self._motion_handler):
            self.disconnect_by_func(self._on_motion_event)

        self._motion_buffer.compute_from_last_motion_event(event)

        if not self.active:
            self.active = True
            return

        if self._event_mode == self.MODE_MOTION:
            speed = self._motion_buffer.speed_y_from_last_motion_event

            # Calculation of the new target according to vertical speed.
            target = self.offset - speed * 200

            if target < 0:
                target = 0
            elif target > self._offset_max:
                target = self._offset_max

            self._update_motion_behaviour(target)
            self._motion_timeline.start()

        return False

    def _on_motion_event(self, actor, event):
        """motion-event handler."""
        # Minimum distance we to move before we consider a motion has started.
        motion_threshold = 10

        self._motion_buffer.compute_from_start(event)
        if self._motion_buffer.distance_from_start > motion_threshold:
            self._motion_buffer.take_new_motion_event(event)
            self._event_mode = self.MODE_MOTION
            self.offset = self._old_offset - self._motion_buffer.dy_from_start

        return False

    def _on_scroll_event(self, actor, event):
        """scroll-event handler (mouse's wheel)."""
        if not self.active:
            self.active = True
            return

        # Do not scroll if there is no need.
        if self._offset_max < 0:
            return False

        if event.direction == clutter.SCROLL_DOWN:
            self.scroll_down()
        else:
            self.scroll_up()

        self.emit('moving')

        return False

    def _motion_timeline_callback(self, timeline, screen):
        """Code executed when the animation is finished."""
        self.offset = -self.content.get_y()
예제 #14
0
class Photographs(Screen):
    '''Screen displays a grid of selectable photograph thumbnails.'''

    def __init__(self, move_to_new_screen_callback, title, images):
        Screen.__init__(self, 'Photographs', move_to_new_screen_callback)

        self.images = images

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, title)
        self.add(screen_title)

        # Image Title (over album list)
        self.image_title = Label(0.04167, "title", 0.0586, 0.7943, " ")
        self.image_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_title)

        self.image_desc = Label(0.04167, "subtitle", 0.0586, 0.9115, " ")
        self.image_desc.set_line_wrap(True)
        self.image_desc.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_desc)

        # Display throbber animation while loading photographs
        self.throbber = LoadingAnimation(0.9, 0.9)
        self.throbber.show()
        self.add(self.throbber)

        # List indicator
        self.li = None
        self._create_list_indicator()

        # Create photomenu
        self.menu = ImageMenu(0.03, 0.08, 0.12, self.y_for_x(0.12))
        self.menu.items_per_col = 3
        self.menu.visible_rows = 3
        self.menu.visible_cols = 8
        self.menu.active = True
        self.add(self.menu)

        photos = self.images
        photos_list = [[Texture(photo.get_thumbnail_url()), photo] \
            for photo in photos]
        self.menu.async_add(photos_list)

        self.menu.connect("selected", self._handle_select)
        self.menu.connect('moved', self._update_image_info)
        self.menu.connect("filled", self._on_menu_filled)

    def _update_image_info(self, event=None):
        """Update image information box."""
        image = self.images[self.menu.selected_index]
        name = image.get_title()
        desc = image.get_description()
        self.image_title.set_text(name)
        self.image_title.set_size(0.366, 0.04167)
        self.image_desc.set_text(desc)
        self.image_desc.set_size(0.366, 0.0911)
        self.li.set_current(self.menu.selected_index + 1)

    def _create_list_indicator(self):
        '''Create list indicator for albums list.'''
        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
            ListIndicator.HORIZONTAL)
        self.li.set_maximum(len(self.images))
        self.add(self.li)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.menu.down()

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        self.menu.left()

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        self.menu.right()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        index = self.menu.selected_index
        kwargs = {'current_photo_index' : index, 'images' : self.images}
        self.callback("photo", kwargs)

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #15
0
class AlbumsTab(Tab):
    '''Tab to show album listings'''

    def __init__(self, albums, move_to_new_screen_callback, name="albums",
        tab_title=_("Albums")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)

        # Start the loading animation while the menu is loading
        self.throbber = LoadingAnimation(0.6, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        if len(albums) < 4:
            x_percent = 0.2928
            visible_rows = 1
            visible_cols = 3
        elif len(albums) < 13:
            x_percent = 0.1464
            visible_rows = 2
            visible_cols = 6
        else:
            x_percent = 0.1098
            visible_rows = 3
            visible_cols = 8

        # Create albums menu
        self.menu = ImageMenu(0.07, 0.16, x_percent, self.y_for_x(x_percent))
        self.menu.visible_rows = visible_rows
        self.menu.visible_cols = visible_cols
        self.menu.items_per_col = self.menu.visible_rows
        self.add(self.menu)

        albums_list = [[album.album_art_url, album] for album in albums]
        self.menu.async_add_albums(albums_list)

        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
            ListIndicator.HORIZONTAL)
        self.li.set_maximum(len(albums))
        self.li.show()
        self.add(self.li)

        # Create album information (displays current menuitem information)
        self.album_title = Label(0.045, "title", 0.22, 0.79, "")
        self.album_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.album_title.set_line_wrap(False)
        self.album_title.width = 0.366
        self.add(self.album_title)

        self.album_artist = Label(0.037, "subtitle", 0.22, 0.86, "")
        self.album_artist.set_ellipsize(pango.ELLIPSIZE_END)
        self.album_artist.set_line_wrap(False)
        self.album_artist.width = 0.366
        self.add(self.album_artist)

        self.album_tracks = Label(0.037, "subtitle", 0.22, 0.91, "")
        self.add(self.album_tracks)

        self.connect('activated', self._on_activated)
        self.connect('deactivated', self._on_deactivated)
        self.menu.connect("moved", self._update_album_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_activated)
        self.menu.connect("filled", self._on_menu_filled)

    def can_activate(self):
        '''Albums tab will always be created from an existing artist with at
        least one album.'''
        return True

    def _update_album_info(self, event=None):
        '''Update the album information labels.'''
        if self.active:
            album = self.menu.selected_userdata
            self.album_title.set_text(album.title)
            self.album_artist.set_text(album.artist)
            self.album_tracks.set_text(_("%(total)s tracks") % \
                {'total': len(album.tracks)})
            self.li.show()
            self.li.set_current(self.menu.selected_index + 1)
        else:
            self.album_title.set_text("")
            self.album_artist.set_text("")
            self.album_tracks.set_text("")
            self.li.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        album = self.menu.selected_userdata
        kwargs = { 'album' : album }
        self.callback("album", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_album_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_album_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #16
0
class ArtistsTab(Tab):
    '''Tab for the music screen to show artist listings'''

    def __init__(self, music_library, artists, move_to_new_screen_callback,
        name="artists", tab_title=_("Artists")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
        self.library = music_library

        # Start the loading animation while the menu is loading
        self.throbber = LoadingAnimation(0.1, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        self.menu = TextMenu(0.057, 0.208, 0.293, 0.078)
        self.menu.items_per_row = 3
        self.menu.visible_rows = 7
        self.menu.visible_cols = 3
        self.menu.active = False
        self.menu.cursor = None
        self.add(self.menu)

        artists_list = [[artist, None, artist] for artist in artists]
        self.menu.async_add_artists(artists_list)

        # Create artist label
        self.artist_title = Label(0.0416, "title", 0.22, 0.794, "")
        self.artist_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.artist_title.set_line_wrap(False)
        self.artist_title.width = 0.366
        self.add(self.artist_title)

        self.artist_albums = Label(0.0365, "subtitle", 0.22, 0.86, "")
        self.add(self.artist_albums)

        self.artist_tracks = Label(0.0365, "subtitle", 0.22, 0.911, "")
        self.add(self.artist_tracks)

        # Create artist menu list indicator
        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
            ListIndicator.VERTICAL)
        self.li.set_maximum(len(artists))
        self.add(self.li)

        self.connect('activated', self._on_activated)
        self.connect('deactivated', self._on_deactivated)
        self.menu.connect("moved", self._update_artist_info)
        self.menu.connect("selected", self._handle_select)
        self.menu.connect("activated", self._on_activated)
        self.menu.connect("filled", self._on_menu_filled)

    def can_activate(self):
        '''Albums tab will always be created from an existing artist with at
        least one album.'''
        return True

    def _update_artist_info(self, event=None):
        '''Update the artist information labels'''
        if self.active:
            artist = self.menu.selected_userdata
            self.artist_title.set_text(artist)
            self.artist_albums.set_text(_("%(albums)d albums") %
                {'albums': self.library.number_of_albums_by_artist(artist)})
            self.artist_tracks.set_text(_("%(tracks)d tracks") %
                {'tracks': self.library.number_of_tracks_by_artist(artist)})
            self.li.show()
            self.li.set_current(self.menu.selected_index + 1)
        else:
            self.artist_title.set_text("")
            self.artist_albums.set_text("")
            self.artist_tracks.set_text("")
            self.li.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        artist = self.menu.selected_userdata
        kwargs = { 'artist' : artist }
        self.callback("artist", kwargs)
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_artist_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_artist_info()

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #17
0
class VideoClipsTab(Tab):
    """
    Tab can be used as part of the TabGroup

    Tab is a very simple container that contains all the widgets and logic
    of the tab page.
    """
    def __init__(self,
                 media_player,
                 video_library,
                 move_to_new_screen_callback,
                 name="clips",
                 tab_title=_("Video clips")):
        Tab.__init__(self, name, tab_title, move_to_new_screen_callback)
        self.media_player = media_player
        self.video_library = video_library
        self.theme = self.config.theme
        self.list_indicator = None
        self.clip_info = None
        self.menu = None
        self.clip_title = None

        if self.video_library.get_number_of_video_clips() == 0:
            self._create_empty_library_notice()
        else:
            # Start the loading animation while the menu is loading
            self.throbber = LoadingAnimation(0.7, 0.1)
            self.throbber.show()
            self.add(self.throbber)

            self.menu = self._create_menu()
            self.add(self.menu)
            self.menu.connect("moved", self._update_clip_info)
            self.menu.connect("selected", self._handle_select)
            self.menu.connect("activated", self._on_activated)
            self.menu.connect("filled", self._on_menu_filled)

            self.connect('activated', self._on_activated)
            self.connect('deactivated', self._on_deactivated)

    def can_activate(self):
        """
        Allow if we have some movies indexed.
        """
        if self.video_library.get_number_of_video_clips() == 0:
            return False
        else:
            return True

    def _create_empty_library_notice(self):
        """
        Create an information box that is displayed if there are no indexed
        movies.
        """
        message = _(
            "There are no indexed Video Clips in the Entertainer media "
            "library. Please add some folders containing video clips "
            "to the Library using the configuration tool.")
        Tab.show_empty_tab_notice(self, _("No video clips available!"),
                                  message)

    def _create_menu(self):
        """
        Create a view that is displayed when there are indexed clips in
        the video library.
        """
        menu = ImageMenu(0.04, 0.16, 0.23, self.y_for_x(0.23) * 0.7)
        menu.items_per_col = 2
        menu.visible_rows = 2
        menu.visible_cols = 4

        clips = self.video_library.get_video_clips()
        clips_list = [[clip.thumbnail_url, clip] for clip in clips]
        menu.async_add_clips(clips_list)

        # Create list indicator
        self.list_indicator = ListIndicator(0.7, 0.8, 0.2, 0.045,
                                            ListIndicator.HORIZONTAL)
        self.list_indicator.set_maximum(len(clips))
        self.list_indicator.show()
        self.add(self.list_indicator)

        # Create information labels
        self.clip_title = Label(0.042,
                                "title",
                                0.15,
                                0.77,
                                "",
                                font_weight="bold")
        self.clip_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.clip_title.set_line_wrap(False)
        self.clip_title.width = 0.5
        self.add(self.clip_title)

        self.clip_info = Label(0.034, "subtitle", 0.15, 0.85, "")
        self.clip_info.set_ellipsize(pango.ELLIPSIZE_END)
        self.clip_info.set_line_wrap(False)
        self.clip_info.width = 0.5
        self.add(self.clip_info)

        return menu

    def _update_clip_info(self, event=None):
        '''Update the VideoClip information labels.'''
        if self.active:
            clip = self.menu.selected_userdata
            (folder, filename) = os.path.split(clip.filename)
            self.clip_title.set_text(filename)
            self.clip_info.set_text(folder)
            self.list_indicator.show()
            self.list_indicator.set_current(self.menu.selected_index + 1)
        else:
            self.clip_title.set_text("")
            self.clip_info.set_text("")
            self.list_indicator.hide()

    def _handle_up(self):
        '''Handle the up user event.'''
        if self.menu.on_top:
            return True  # Move control back to tab bar
        else:
            self.menu.up()
            return False

    def _handle_down(self):
        '''Handle the down user event.'''
        self.menu.down()
        return False

    def _handle_left(self):
        '''Handle the left user event.'''
        self.menu.left()
        return False

    def _handle_right(self):
        '''Handle the right user event.'''
        self.menu.right()
        return False

    def _handle_select(self, event=None):
        '''Handle the select user event.'''
        clip = self.menu.selected_userdata
        self.media_player.set_media(clip)
        self.media_player.play()
        self.callback("video_osd")
        return False

    def _on_activated(self, event=None):
        '''Tab activated.'''
        if self.tab_group is not None:
            self.tab_group.active = False
        self.menu.active = True
        self.active = True
        self._update_clip_info()
        return False

    def _on_deactivated(self, event=None):
        '''Tab deactivated.'''
        self.active = False
        self.menu.active = False
        self._update_clip_info()
        return False

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #18
0
class Photographs(Screen):
    '''Screen displays a grid of selectable photograph thumbnails.'''
    def __init__(self, move_to_new_screen_callback, title, images):
        Screen.__init__(self, 'Photographs', move_to_new_screen_callback)

        self.images = images

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, title)
        self.add(screen_title)

        # Image Title (over album list)
        self.image_title = Label(0.04167, "title", 0.0586, 0.7943, " ")
        self.image_title.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_title)

        self.image_desc = Label(0.04167, "subtitle", 0.0586, 0.9115, " ")
        self.image_desc.set_line_wrap(True)
        self.image_desc.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(self.image_desc)

        # Display throbber animation while loading photographs
        self.throbber = LoadingAnimation(0.9, 0.9)
        self.throbber.show()
        self.add(self.throbber)

        # List indicator
        self.li = None
        self._create_list_indicator()

        # Create photomenu
        self.menu = ImageMenu(0.03, 0.08, 0.12, self.y_for_x(0.12))
        self.menu.items_per_col = 3
        self.menu.visible_rows = 3
        self.menu.visible_cols = 8
        self.menu.active = True
        self.add(self.menu)

        photos = self.images
        photos_list = [[Texture(photo.get_thumbnail_url()), photo] \
            for photo in photos]
        self.menu.async_add(photos_list)

        self.menu.connect("selected", self._handle_select)
        self.menu.connect('moved', self._update_image_info)
        self.menu.connect("filled", self._on_menu_filled)

    def _update_image_info(self, event=None):
        """Update image information box."""
        image = self.images[self.menu.selected_index]
        name = image.get_title()
        desc = image.get_description()
        self.image_title.set_text(name)
        self.image_title.set_size(0.366, 0.04167)
        self.image_desc.set_text(desc)
        self.image_desc.set_size(0.366, 0.0911)
        self.li.set_current(self.menu.selected_index + 1)

    def _create_list_indicator(self):
        '''Create list indicator for albums list.'''
        self.li = ListIndicator(0.77, 0.8, 0.18, 0.045,
                                ListIndicator.HORIZONTAL)
        self.li.set_maximum(len(self.images))
        self.add(self.li)

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.menu.down()

    def _handle_left(self):
        '''Handle UserEvent.NAVIGATE_LEFT.'''
        self.menu.left()

    def _handle_right(self):
        '''Handle UserEvent.NAVIGATE_RIGHT.'''
        self.menu.right()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        index = self.menu.selected_index
        kwargs = {'current_photo_index': index, 'images': self.images}
        self.callback("photo", kwargs)

    def _on_menu_filled(self, event=None):
        '''Handles filled event.'''
        self.throbber.hide()
예제 #19
0
class ScrollArea(Base, clutter.Group):
    """Wrapper of a clutter Group that allows for scrolling. ScrollArea
    modifies the width of the content and it assumes that the content uses
    percent modification (read: not default clutter objects)."""
    __gsignals__ = {
        'activated': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
        'moving': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
    }

    MODE_SELECTION = 0
    MODE_MOTION = 1
    MODE_STOP = 2
    STEP_SIZE_PERCENT = 0.04

    def __init__(self, x, y, width, height, content):
        Base.__init__(self)
        clutter.Group.__init__(self)

        self._motion_buffer = MotionBuffer()
        self._offset = 0  # Drives the content motion.
        self._offset_max = 0  # Maximum value of offset (equal on bottom).
        self._old_offset = 0  # Stores the old value of offset on motions.
        self._motion_handler = 0
        self._active = None

        self.step_size = self.get_abs_y(self.STEP_SIZE_PERCENT)

        # Allowed area for the widget's scrolling content.
        self.area_width = self.get_abs_x(width)
        self.area_height = self.get_abs_y(height)

        # Create content position indicator
        self.indicator = ListIndicator(3 * width / 4, height, 0.2, 0.045,
                                       ListIndicator.VERTICAL)
        self.indicator.hide_position()
        self.indicator.set_maximum(2)
        self.add(self.indicator)

        # A clipped Group to receive the content.
        self._fixed_group = clutter.Group()
        self._fixed_group.set_clip(0, 0, self.area_width, self.area_height)
        self.add(self._fixed_group)
        self.content = None

        self._motion_timeline = clutter.Timeline(500)
        self._motion_timeline.connect('completed',
                                      self._motion_timeline_callback, None)
        self._motion_alpha = clutter.Alpha(self._motion_timeline,
                                           clutter.EASE_OUT_SINE)
        self._motion_behaviour = LoopedPathBehaviour(self._motion_alpha)

        self.set_content(content)

        self.active = None

        # Preparation to pointer events handling.
        self.set_reactive(True)
        self.connect('button-press-event', self._on_button_press_event)
        self.connect('button-release-event', self._on_button_release_event)
        self.connect('scroll-event', self._on_scroll_event)

        self.set_position(self.get_abs_x(x), self.get_abs_y(y))

    @property
    def on_top(self):
        """True if we're on top."""
        return self._offset == 0

    @property
    def on_bottom(self):
        """True if we're on bottom."""
        return self._offset == self._offset_max

    def _get_active(self):
        """Active property getter."""
        return self._active

    def _set_active(self, boolean):
        """Active property setter."""
        if self._active == boolean:
            return

        self._active = boolean
        if boolean:
            # Show indicator if there is need for scrolling.
            if self._offset_max >= 0:
                self.indicator.show()

            self.set_opacity(255)
            self.emit('activated')
        else:
            self.indicator.hide()
            self.set_opacity(128)

    active = property(_get_active, _set_active)

    def _get_offset(self):
        """Get current offset value."""
        return self._offset

    def _set_offset(self, integer):
        """Set current offset value."""
        if self._offset == integer:
            return

        self._offset = integer

        if self._offset < 0:
            self._offset = 0
        elif self._offset > self._offset_max:
            self._offset = self._offset_max

        self.content.set_position(0, -self._offset)

        # Indicator updates.
        if self.on_top:
            self.indicator.set_current(1)
        elif self.on_bottom:
            self.indicator.set_current(2)

    offset = property(_get_offset, _set_offset)

    def set_content(self, content):
        """Set content into scroll area."""
        if self.content is not None:
            self._fixed_group.remove(self.content)
            self._motion_behaviour.remove(self.content)

        self.content = content
        self._fixed_group.add(content)

        self._offset_max = self.content.get_height() - self.area_height

        self._motion_behaviour.apply(self.content)

    def stop_animation(self):
        """Stops the timeline driving animation."""
        self._motion_timeline.stop()

    def scroll_to_top(self):
        """Scroll content back to top."""
        self.offset = 0

    def scroll_to_bottom(self):
        """Scroll content as much as possible."""
        self.offset = self._offset_max

    def scroll_up(self):
        """Scroll up by one step size."""
        self.offset -= self.step_size

    def scroll_down(self):
        """Scroll down by one step size."""
        self.offset += self.step_size

    def scroll_page_up(self):
        """Scroll up by one page. Page is a scroll area height."""
        self.offset -= self.area_height

    def scroll_page_down(self):
        self.offset += self.area_height

    def _update_motion_behaviour(self, target):
        """Preparation of looped behaviour applied to the content."""
        self._motion_behaviour.start_knot = (0.0, -self.offset)
        self._motion_behaviour.end_knot = (0.0, -target)
        self._motion_behaviour.start_index = 0.0
        # Need to set the end index to 0.9999. Indeed the LoopedPathBehaviour
        # uses an index in [0, 1[. So index = 1 is equivalent to index = 0, the
        # Actor will the be placed on the start_knot.
        self._motion_behaviour.end_index = 0.9999

    def _on_button_press_event(self, actor, event):
        """button-press-event handler."""
        clutter.grab_pointer(self)
        if not self.handler_is_connected(self._motion_handler):
            self._motion_handler = self.connect('motion-event',
                                                self._on_motion_event)

        if self._motion_timeline.is_playing():
            # A click with an animation pending should stop the animation.
            self._motion_timeline.stop()

            # Go to MODE_STOP to handle correctly next button-release event.
            self._event_mode = self.MODE_STOP
            self.offset = -self.content.get_y()
        else:
            # No animation pending so we're going to do nothing or to move
            # all the content.
            self._old_offset = self.offset
            self._motion_buffer.start(event)
            self._event_mode = self.MODE_SELECTION

        return False

    def _on_button_release_event(self, actor, event):
        """button-release-event handler."""
        clutter.ungrab_pointer()
        if self.handler_is_connected(self._motion_handler):
            self.disconnect_by_func(self._on_motion_event)

        self._motion_buffer.compute_from_last_motion_event(event)

        if not self.active:
            self.active = True
            return

        if self._event_mode == self.MODE_MOTION:
            speed = self._motion_buffer.speed_y_from_last_motion_event

            # Calculation of the new target according to vertical speed.
            target = self.offset - speed * 200

            if target < 0:
                target = 0
            elif target > self._offset_max:
                target = self._offset_max

            self._update_motion_behaviour(target)
            self._motion_timeline.start()

        return False

    def _on_motion_event(self, actor, event):
        """motion-event handler."""
        # Minimum distance we to move before we consider a motion has started.
        motion_threshold = 10

        self._motion_buffer.compute_from_start(event)
        if self._motion_buffer.distance_from_start > motion_threshold:
            self._motion_buffer.take_new_motion_event(event)
            self._event_mode = self.MODE_MOTION
            self.offset = self._old_offset - self._motion_buffer.dy_from_start

        return False

    def _on_scroll_event(self, actor, event):
        """scroll-event handler (mouse's wheel)."""
        if not self.active:
            self.active = True
            return

        # Do not scroll if there is no need.
        if self._offset_max < 0:
            return False

        if event.direction == clutter.SCROLL_DOWN:
            self.scroll_down()
        else:
            self.scroll_up()

        self.emit('moving')

        return False

    def _motion_timeline_callback(self, timeline, screen):
        """Code executed when the animation is finished."""
        self.offset = -self.content.get_y()
예제 #20
0
class Disc(Screen):
    '''Screen allows user to play tracks from the current Audio CD.'''

    def __init__(self, music_library, media_player):
        Screen.__init__(self, 'Disc')

        self.theme = self.config.theme
        self.music_library = music_library
        self.media_player = media_player
        # When album info is loaded we create Playlist object
        self.playlist = None

        self.art = None
        self.art2 = None
        self.in_behaviour = None
        self.out_behaviour = None
        self.li = None
        self.track_menu = None

        # Screen Title (Displayed at the bottom left corner)
        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Audio Disc"),
            "screen_title")
        self.add(screen_title)

        # Display throbber animation while loading CD metadata
        self.throbber = LoadingAnimation(0.5, 0.5, 0.1)
        self.throbber.show()
        self.add(self.throbber)

        # Create and initialize screen items
        self.has_disc = True
        gobject.timeout_add(500, self._get_disc_information)

    def _get_disc_information(self):
        """
        Fetch album information from the Internet and create widgets based
        on received data.
        """
        try:
            disc = self.music_library.get_compact_disc_information()

            title = disc.title
            artist = disc.artist
            tracks = disc.tracks

            self.playlist = Playlist(tracks)
            self._create_album_info(title, artist, tracks, disc.length)
            self.track_menu = self._create_track_menu(tracks)
            self.add(self.track_menu)
            self._create_album_cover_texture(artist, title)

            self.li = ListIndicator(0.75, 0.8, 0.2, 0.045,
                ListIndicator.VERTICAL)
            self.li.set_maximum(len(tracks))
            self.add(self.li)

            art_file = os.path.join(self.config.ALBUM_ART_DIR,
                artist + " - " + title + ".jpg")
            if artist != "Unknown artist" and not os.path.exists(art_file):
                art_search = AlbumArtDownloader(title, artist,
                    self.config.ALBUM_ART_DIR, self._update_albumart)
                art_search.start()

        except cdrom.error:
            # No disc in drive
            self.has_disc = False
            no_disc = Label(0.05, "title", 0.5, 0.5,
                _("No audio disc in drive"))
            no_disc.set_anchor_point_from_gravity(clutter.GRAVITY_CENTER)
            self.add(no_disc)

        # Remove loading animation
        self.throbber.hide()
        self.remove(self.throbber)
        del self.throbber

        # This function should be called only once (gobject timeout)
        return False

    def _update_albumart(self, artist, title):
        """
        Search album art for current audio disc. This function is called only
        if album art doesn't exist already. If album art is found then we
        replace current disc icon with the new album art.
        @param artist: Artist name
        @param title: Album title
        """
        art_file = os.path.join(self.config.ALBUM_ART_DIR,
            artist + " - " + title + ".jpg")

        if os.path.exists(art_file):
            clutter.threads_enter()
            self.art2 = Texture(art_file, 0.1, 0.165)
            clutter.threads_leave()

            self.art2.set_size(self.get_abs_x(0.3148), self.get_abs_y(0.5599))
            self.art2.set_opacity(0)
            self.add(self.art2)

            timeline_in = clutter.Timeline(35, 26)
            alpha_in = clutter.Alpha(timeline_in, clutter.smoothstep_inc_func)
            self.in_behaviour = clutter.BehaviourOpacity(0, 255, alpha_in)
            self.in_behaviour.apply(self.art2)

            timeline_out = clutter.Timeline(35, 26)
            alpha_out = clutter.Alpha(timeline_out, clutter.smoothstep_inc_func)
            self.out_behaviour = clutter.BehaviourOpacity(255, 0, alpha_out)
            self.out_behaviour.apply(self.art)

            timeline_out.start()
            timeline_in.start()

    def _create_album_cover_texture(self, artist, title):
        """
        Create a texture that is displayed next to track list. This texture
        displays album cover art.
        @param artist: Artist
        @param title: Title of the album
        """
        coverfile = os.path.join(self.config.ALBUM_ART_DIR,
            artist + " - " + title + ".jpg")

        if(os.path.exists(coverfile)):
            pixbuf = gtk.gdk.pixbuf_new_from_file(coverfile)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.theme.getImage("disc"))
        self.art = EyeCandyTexture(0.1, 0.13, 0.3148, 0.5599, pixbuf)
        self.art.set_rotation(clutter.Y_AXIS, 25, 0, 0, 0)
        self.add(self.art)

    def _create_album_info(self, title, artist_name, tracks, length):
        """
        Create album info labels.
        @param title: Album title
        @param artist_name: Artist
        @param tracks: List of CompactDisc objects
        """
        album = Label(0.04167, "text", 0.50146, 0.13,
           artist_name + " - " + title, font_weight="bold")
        album.set_size(0.4393, 0.06510)
        album.set_ellipsize(pango.ELLIPSIZE_END)
        self.add(album)

        minutes = str(length / 60)

        num_of_tracks = Label(0.02604, "subtitle", 0.50146, 0.18,
            _("%(total)s tracks, %(time)s minutes") % \
            {'total': len(tracks), 'time': minutes}, font_weight="bold")
        self.add(num_of_tracks)

    def _create_track_menu(self, tracks):
        """
        Create a track menu. This menu contains list of all tracks on album.
        @param tracks: List of CompactDisc objects
        """
        menu = TextMenu(0.4978, 0.2344, 0.4393, 0.0781)
        menu.visible_rows = 7

        tracks_list = [[track.title, track.length_string, index] \
            for index, track in enumerate(tracks)]
        menu.async_add(tracks_list)

        menu.active = True
        menu.connect('selected', self._handle_select)
        menu.connect('moved', self._display_selected_track)

        return menu

    def _handle_up(self):
        '''Handle UserEvent.NAVIGATE_UP.'''
        self.track_menu.up()

    def _handle_down(self):
        '''Handle UserEvent.NAVIGATE_DOWN.'''
        self.track_menu.down()

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        track_index = self.track_menu.selected_userdata
        self.playlist.set_current(track_index)
        self.media_player.set_playlist(self.playlist)
        self.media_player.play()

    def handle_user_event(self, event):
        '''Handle user events unless there is no disc.'''
        if self.has_disc:
            Screen.handle_user_event(self, event)

    def _display_selected_track(self, event=None):
        '''Update of the list indicator.'''
        self.li.set_current(self.track_menu.selected_index + 1)