Example #1
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()
Example #2
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()
Example #3
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()
Example #4
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()
Example #5
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()
Example #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()
Example #7
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()
Example #8
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()
Example #9
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()
Example #10
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()