예제 #1
0
    def create_menu(self):
        """
        Create menu of the screen. This displayed the left side of the screen.
        """
        self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781)
        self.menu.add_item(_("Watch"), None, "watch")
        self.menu.active = True

        self.add(self.menu)
예제 #2
0
    def display_answers(self):
        '''Display a menu with answers on the screen.'''
        self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781)
        self.menu.set_name("questionmenu")
        self.menu.connect('selected', self._handle_select)

        for answer in self.answers:
            self.menu.add_item(str(answer), None, str(answer))

        self.menu.active = True
        self.add(self.menu)
예제 #3
0
    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
예제 #4
0
    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
예제 #5
0
    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
예제 #6
0
    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
예제 #7
0
    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
예제 #8
0
    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)
예제 #9
0
class TextMenuTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.text_menu"""
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.menu = TextMenu()

    def test_create(self):
        '''Test correct TextMenu initialization.'''
        self.assertTrue(isinstance(self.menu, TextMenu))

    def test_add_item(self):
        '''Test the add_item method.'''
        items = self.menu.count
        self.menu.add_item("text", "extra_text", "data")
        self.assertEqual(self.menu.count, items + 1)
예제 #10
0
    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
예제 #11
0
    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)
예제 #12
0
class TextMenuTest(EntertainerTest):
    """Test for entertainerlib.gui.widgets.text_menu"""

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

        self.menu = TextMenu()

    def test_create(self):
        '''Test correct TextMenu initialization.'''
        self.assertTrue(isinstance(self.menu, TextMenu))

    def test_add_item(self):
        '''Test the add_item method.'''
        items = self.menu.count
        self.menu.add_item("text", "extra_text", "data")
        self.assertEqual(self.menu.count, items + 1)
예제 #13
0
    def display_answers(self):
        '''Display a menu with answers on the screen.'''
        self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781)
        self.menu.set_name("questionmenu")
        self.menu.connect('selected', self._handle_select)

        for answer in self.answers:
            self.menu.add_item(str(answer), None, str(answer))

        self.menu.active = True
        self.add(self.menu)
예제 #14
0
    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
예제 #15
0
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.menu = TextMenu()
예제 #16
0
class Question(Screen):
    '''Screen is displayed when the application needs to ask a close ended
    question to the user.'''
    def __init__(self,
                 move_to_previous_screen_callback,
                 question,
                 answers,
                 callbacks=None):
        Screen.__init__(self, 'Question')

        self.move_to_previous_screen_callback = move_to_previous_screen_callback
        self.answers = answers

        if callbacks is None:
            callbacks = []
        self.callbacks = callbacks

        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Question"))
        self.add(screen_title)

        question_label = Label(0.04167, "text", 0.095, 0.13, str(question))
        self.add(question_label)

        self.menu = None
        self.display_answers()

    def display_answers(self):
        '''Display a menu with answers on the screen.'''
        self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781)
        self.menu.set_name("questionmenu")
        self.menu.connect('selected', self._handle_select)

        for answer in self.answers:
            self.menu.add_item(str(answer), None, str(answer))

        self.menu.active = True
        self.add(self.menu)

    def question_callback(self, selected_answer):
        '''Call the method associated with the selected answer.'''
        callback_loc = 0
        for answer in self.answers:
            if str(answer) == selected_answer:
                break
            callback_loc += 1

        if callback_loc < len(self.callbacks):
            callback = self.callbacks[callback_loc]
            if callback != None:
                callback()

    def go_back(self):
        '''Go back to previous screen'''
        self.move_to_previous_screen_callback()

    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.'''
        self.question_callback(self.menu.selected_userdata)
        self.go_back()
예제 #17
0
class Movie(Screen):
    '''Screen contains information of one movie.'''

    def __init__(self, media_player, movie, move_to_new_screen_callback):
        Screen.__init__(self, 'Movie', move_to_new_screen_callback)

        self.theme = self.config.theme
        self.media_player = media_player
        self.movie = movie

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

        # Add the additional actions that are needed but not handled by default
        self.event_handlers.update({
            UserEvent.NAVIGATE_FIRST_PAGE : self._handle_first_page,
            UserEvent.NAVIGATE_LAST_PAGE : self._handle_last_page,
            UserEvent.NAVIGATE_PREVIOUS_PAGE : self._handle_previous_page,
            UserEvent.NAVIGATE_NEXT_PAGE : self._handle_next_page
        })

        self.menu = None
        self.scroll_area = None
        self.create_menu()
        self.create_movie_information()

    def create_movie_information(self):
        '''Create clutter parts related to movie information'''

        # Movie art texture
        if self.movie.has_cover_art():
            pixbuf = gtk.gdk.pixbuf_new_from_file(self.movie.cover_art_url)
        else:
            pixbuf = gtk.gdk.pixbuf_new_from_file(
                self.theme.getImage("default_movie_art"))
        movie_art = EyeCandyTexture(0.33, 0.1, 0.1, 0.25, pixbuf)
        self.add(movie_art)

        # Movie title
        title = Label(0.04, "title", 0.47, 0.1, self.movie.title,
            font_weight="bold")
        title.set_ellipsize(pango.ELLIPSIZE_END)
        title.set_size(0.5124, 0.05208)
        self.add(title)

        # Movie release year
        year_text = _("Released in %(year)s") % {'year': self.movie.year}
        year = Label(0.032, "subtitle", 0.47, 0.3, year_text)
        year.set_ellipsize(pango.ELLIPSIZE_END)
        year.set_size(0.5124, 0.05208)
        self.add(year)

        # Show only 2 genres (or one if there is only one)
        genres_list = self.movie.genres
        if len(genres_list) == 0:
            genres_text = _("Unknown")
        else:
            genres_text = "/".join(genres_list[:2])

        # Runtime and genres
        info_text = _("%(runtime)s min, %(genre)s") % \
            {'runtime': self.movie.runtime, 'genre': genres_text}
        info = Label(0.032, "subtitle", 0.47, 0.24, info_text)
        info.set_ellipsize(pango.ELLIPSIZE_END)
        info.set_size(0.5124, 0.05208)
        self.add(info)

        # Stars (rating)
        star = Texture(self.theme.getImage("star"))
        star.hide()
        self.add(star)
        star2 = Texture(self.theme.getImage("star2"))
        star2.hide()
        self.add(star2)

        for i in range(self.movie.rating):
            tex = clutter.Clone(star)
            tex.set_position(
                self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * i),
                self.get_abs_y(0.17))
            tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
            self.add(tex)

        dark_star = 5 - self.movie.rating
        for i in range(dark_star):
            tex = clutter.Clone(star2)
            tex.set_position(self.get_abs_x(0.47) + (self.get_abs_x(0.0366) * \
                (i + self.movie.rating)), self.get_abs_y(0.17))
            tex.set_size(self.get_abs_x(0.024), self.get_abs_y(0.04))
            self.add(tex)

        # Plot
        plot = Label(0.029, "subtitle", 0, 0, self.movie.plot)
        plot.set_justify(True)
        plot.set_line_wrap_mode(pango.WRAP_WORD)
        plot.set_line_wrap(True)
        plot.width = 0.5124
        self.scroll_area = ScrollArea(0.33, 0.38, 0.5124, 0.3516, plot)
        self.add(self.scroll_area)

        # Actors
        self.add(Label(0.032, "title", 0.33, 0.8, _("Starring")))

        actors_list = self.movie.actors
        if len(actors_list) == 0:
            actors_text = _("Unknown")
        else:
            actors_text = ", ".join(actors_list[:5])
        actors = Label(0.032, "subtitle", 0.46, 0.8, actors_text)
        actors.set_ellipsize(pango.ELLIPSIZE_END)
        actors.set_size(0.5124, 0.05208)
        self.add(actors)

        # Directors
        self.add(Label(0.032, "title", 0.33, 0.86, _("Directed by")))

        directors_list = self.movie.directors
        if len(directors_list) == 0:
            directors_text = _("Unknown")
        else:
            directors_text = ", ".join(directors_list[:2])
        directors = Label(0.032, "subtitle", 0.46, 0.86, directors_text)
        directors.set_ellipsize(pango.ELLIPSIZE_END)
        directors.set_size(0.5124, 0.05208)
        self.add(directors)

        # Writers
        self.add(Label(0.032, "title", 0.33, 0.92, _("Written by")))

        writers_list = self.movie.writers
        if len(directors_list) == 0:
            writers_text = _("Unknown")
        else:
            writers_text = ", ".join(writers_list[:2])
        writers = Label(0.032, "subtitle", 0.46, 0.92, writers_text)
        writers.set_ellipsize(pango.ELLIPSIZE_END)
        writers.set_size(0.5124, 0.05208)
        self.add(writers)

    def create_menu(self):
        """
        Create menu of the screen. This displayed the left side of the screen.
        """
        self.menu = TextMenu(0.07, 0.1, 0.2196, 0.0781)
        self.menu.add_item(_("Watch"), None, "watch")
        self.menu.active = True

        self.add(self.menu)

    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 = True
        self.scroll_area.active = False

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

    def _handle_select(self, event=None):
        '''Handle UserEvent.NAVIGATE_SELECT.'''
        if self.menu.active:
            item = self.menu.selected_userdata
            if item == "watch":
                self.media_player.set_media(self.movie)
                self.media_player.play()
                self.callback("video_osd")

    def _handle_first_page(self):
        '''Handle UserEvent.NAVIGATE_FIRST_PAGE.'''
        self.scroll_area.scroll_to_top()

    def _handle_last_page(self):
        '''Handle UserEvent.NAVIGATE_LAST_PAGE.'''
        self.scroll_area.scroll_to_bottom()

    def _handle_previous_page(self):
        '''Handle UserEvent.NAVIGATE_PREVIOUS_PAGE.'''
        self.scroll_area.scroll_page_up()

    def _handle_next_page(self):
        '''Handle UserEvent.NAVIGATE_NEXT_PAGE.'''
        self.scroll_area.scroll_page_down()
예제 #18
0
class Question(Screen):
    '''Screen is displayed when the application needs to ask a close ended
    question to the user.'''

    def __init__(self, move_to_previous_screen_callback, question,
        answers, callbacks=None):
        Screen.__init__(self, 'Question')

        self.move_to_previous_screen_callback = move_to_previous_screen_callback
        self.answers = answers

        if callbacks is None:
            callbacks = []
        self.callbacks = callbacks

        screen_title = Label(0.13, "screentitle", 0, 0.87, _("Question"))
        self.add(screen_title)

        question_label = Label(0.04167, "text", 0.095, 0.13, str(question))
        self.add(question_label)

        self.menu = None
        self.display_answers()

    def display_answers(self):
        '''Display a menu with answers on the screen.'''
        self.menu = TextMenu(0.095, 0.5, 0.4393, 0.0781)
        self.menu.set_name("questionmenu")
        self.menu.connect('selected', self._handle_select)

        for answer in self.answers:
            self.menu.add_item(str(answer), None, str(answer))

        self.menu.active = True
        self.add(self.menu)

    def question_callback(self, selected_answer):
        '''Call the method associated with the selected answer.'''
        callback_loc = 0
        for answer in self.answers:
            if str(answer) == selected_answer:
                break
            callback_loc += 1

        if callback_loc < len(self.callbacks):
            callback = self.callbacks[callback_loc]
            if callback != None:
                callback()

    def go_back(self):
        '''Go back to previous screen'''
        self.move_to_previous_screen_callback()

    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.'''
        self.question_callback(self.menu.selected_userdata)
        self.go_back()
예제 #19
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()
예제 #20
0
    def setUp(self):
        '''Set up the test.'''
        EntertainerTest.setUp(self)

        self.menu = TextMenu()
예제 #21
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()